Bladeren bron

Feat/provider add zhipuai (#1192)

Co-authored-by: Joel <iamjoel007@gmail.com>
tags/0.3.23
zxhlyh 2 jaren geleden
bovenliggende
commit
60e0bbd713
No account linked to committer's email address
22 gewijzigde bestanden met toevoegingen van 466 en 126 verwijderingen
  1. 3
    43
      web/app/(commonLayout)/apps/Apps.tsx
  2. 1
    1
      web/app/components/app/configuration/config-model/index.tsx
  3. 38
    7
      web/app/components/app/configuration/config-model/param-item.tsx
  4. 8
    0
      web/app/components/base/icons/assets/public/llm/zhipuai-text-cn.svg
  5. 6
    0
      web/app/components/base/icons/assets/public/llm/zhipuai-text.svg
  6. 8
    0
      web/app/components/base/icons/assets/public/llm/zhipuai.svg
  7. 53
    0
      web/app/components/base/icons/src/public/llm/Zhipuai.json
  8. 16
    0
      web/app/components/base/icons/src/public/llm/Zhipuai.tsx
  9. 44
    0
      web/app/components/base/icons/src/public/llm/ZhipuaiText.json
  10. 16
    0
      web/app/components/base/icons/src/public/llm/ZhipuaiText.tsx
  11. 62
    0
      web/app/components/base/icons/src/public/llm/ZhipuaiTextCn.json
  12. 16
    0
      web/app/components/base/icons/src/public/llm/ZhipuaiTextCn.tsx
  13. 3
    0
      web/app/components/base/icons/src/public/llm/index.ts
  14. 2
    0
      web/app/components/header/account-setting/model-page/configs/index.ts
  15. 55
    0
      web/app/components/header/account-setting/model-page/configs/zhipuai.tsx
  16. 1
    0
      web/app/components/header/account-setting/model-page/declarations.ts
  17. 4
    2
      web/app/components/header/account-setting/model-page/index.tsx
  18. 4
    0
      web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx
  19. 1
    1
      web/app/components/header/account-setting/model-page/model-item/Setting.tsx
  20. 0
    71
      web/hooks/use-pay.ts
  21. 124
    0
      web/hooks/use-pay.tsx
  22. 1
    1
      web/service/common.ts

+ 3
- 43
web/app/(commonLayout)/apps/Apps.tsx Bestand weergeven

@@ -1,7 +1,6 @@
'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>
</>

+ 1
- 1
web/app/components/app/configuration/config-model/index.tsx Bestand weergeven

@@ -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)


+ 38
- 7
web/app/components/app/configuration/config-model/param-item.tsx Bestand weergeven

@@ -1,9 +1,20 @@
'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)

+ 8
- 0
web/app/components/base/icons/assets/public/llm/zhipuai-text-cn.svg
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 6
- 0
web/app/components/base/icons/assets/public/llm/zhipuai-text.svg
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 8
- 0
web/app/components/base/icons/assets/public/llm/zhipuai.svg
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 53
- 0
web/app/components/base/icons/src/public/llm/Zhipuai.json
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 16
- 0
web/app/components/base/icons/src/public/llm/Zhipuai.tsx Bestand weergeven

@@ -0,0 +1,16 @@
// 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

+ 44
- 0
web/app/components/base/icons/src/public/llm/ZhipuaiText.json
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 16
- 0
web/app/components/base/icons/src/public/llm/ZhipuaiText.tsx Bestand weergeven

@@ -0,0 +1,16 @@
// 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

+ 62
- 0
web/app/components/base/icons/src/public/llm/ZhipuaiTextCn.json
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 16
- 0
web/app/components/base/icons/src/public/llm/ZhipuaiTextCn.tsx Bestand weergeven

@@ -0,0 +1,16 @@
// 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

+ 3
- 0
web/app/components/base/icons/src/public/llm/index.ts Bestand weergeven

@@ -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'

+ 2
- 0
web/app/components/header/account-setting/model-page/configs/index.ts Bestand weergeven

@@ -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,
}

+ 55
- 0
web/app/components/header/account-setting/model-page/configs/zhipuai.tsx Bestand weergeven

@@ -0,0 +1,55 @@
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

+ 1
- 0
web/app/components/header/account-setting/model-page/declarations.ts Bestand weergeven

@@ -42,6 +42,7 @@ export enum ProviderEnum {
'xinference' = 'xinference',
'openllm' = 'openllm',
'localai' = 'localai',
'zhipuai' = 'zhipuai',
}

export type ProviderConfigItem = {

+ 4
- 2
web/app/components/header/account-setting/model-page/index.tsx Bestand weergeven

@@ -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,

+ 4
- 0
web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx Bestand weergeven

@@ -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

+ 1
- 1
web/app/components/header/account-setting/model-page/model-item/Setting.tsx Bestand weergeven

@@ -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}

+ 0
- 71
web/hooks/use-pay.ts Bestand weergeven

@@ -1,71 +0,0 @@
'use client'

import { useEffect, useState } from 'react'
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 type { ConfirmCommonProps } from '@/app/components/base/confirm/common'

export type ConfirmType = Pick<ConfirmCommonProps, 'type' | 'title' | 'desc'>

export const useAnthropicCheckPay = () => {
const { t } = useTranslation()
const [confirm, setConfirm] = useState<ConfirmType | null>(null)
const searchParams = useSearchParams()
const providerName = searchParams.get('provider_name')
const paymentResult = searchParams.get('payment_result')

useEffect(() => {
if (providerName === ProviderEnum.anthropic && (paymentResult === 'succeeded' || paymentResult === 'cancelled')) {
setConfirm({
type: paymentResult === 'succeeded' ? 'success' : 'danger',
title: paymentResult === 'succeeded' ? t('common.actionMsg.paySucceeded') : t('common.actionMsg.payCancelled'),
})
}
}, [providerName, paymentResult, t])

return confirm
}

const QUOTA_RECEIVE_STATUS = {
success: {
'en': 'Anthropic',
'zh-Hans': '领取成功,将在 5 分钟后自动增加配额',
},
fail: {
'en': 'Anthropic',
'zh-Hans': '领取失败',
},
}

export const useSparkCheckQuota = () => {
const { locale } = useContext(I18n)
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 result = searchParams.get('result')

useEffect(() => {
if (type === 'provider_apply_callback' && provider === ProviderEnum.spark && result === 'success')
setShouldVerify(true)
}, [type, provider, result])

return data
? {
type: data.flag ? 'success' : 'danger',
title: data.flag ? QUOTA_RECEIVE_STATUS.success[locale] : QUOTA_RECEIVE_STATUS.fail[locale],
desc: !data.flag ? data.reason : undefined,
}
: null
}

+ 124
- 0
web/hooks/use-pay.tsx Bestand weergeven

@@ -0,0 +1,124 @@
'use client'

import { useCallback, useEffect, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
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 { 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'>

export const useAnthropicCheckPay = () => {
const { t } = useTranslation()
const [confirm, setConfirm] = useState<ConfirmType | null>(null)
const searchParams = useSearchParams()
const providerName = searchParams.get('provider_name')
const paymentResult = searchParams.get('payment_result')

useEffect(() => {
if (providerName === ProviderEnum.anthropic && (paymentResult === 'succeeded' || paymentResult === 'cancelled')) {
setConfirm({
type: paymentResult === 'succeeded' ? 'success' : 'danger',
title: paymentResult === 'succeeded' ? t('common.actionMsg.paySucceeded') : t('common.actionMsg.payCancelled'),
})
}
}, [providerName, paymentResult, t])

return confirm
}

const QUOTA_RECEIVE_STATUS = {
[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': '领取失败',
},
},
[ProviderEnum.zhipuai]: {
success: {
'en': 'Successful collection',
'zh-Hans': '领取成功',
},
fail: {
'en': 'Failure to collect',
'zh-Hans': '领取失败',
},
},
}

const FREE_CHECK_PROVIDER = [ProviderEnum.spark, ProviderEnum.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 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' && FREE_CHECK_PROVIDER.includes(provider) && 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],
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')) || ''}
/>
)
}

+ 1
- 1
web/service/common.ts Bestand weergeven

@@ -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 }>
}

Laden…
Annuleren
Opslaan