Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
e5e86fc0
Unverified
Commit
e5e86fc0
authored
Aug 14, 2023
by
zxhlyh
Committed by
GitHub
Aug 14, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feat/apply free quota (#828)
Co-authored-by:
Joel
<
iamjoel007@gmail.com
>
parent
cc52cdc2
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
196 additions
and
54 deletions
+196
-54
page.tsx
...onLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
+0
-2
Apps.tsx
web/app/(commonLayout)/apps/Apps.tsx
+10
-1
index.tsx
web/app/components/app/overview/apikey-info-panel/index.tsx
+28
-24
spark.tsx
...nents/header/account-setting/model-page/configs/spark.tsx
+10
-10
declarations.ts
...ponents/header/account-setting/model-page/declarations.ts
+4
-0
FreeQuota.tsx
...eader/account-setting/model-page/model-item/FreeQuota.tsx
+100
-0
Setting.tsx
.../header/account-setting/model-page/model-item/Setting.tsx
+13
-0
index.tsx
...ts/header/account-setting/model-page/model-item/index.tsx
+2
-0
index.ts
web/config/index.ts
+1
-0
app-overview.en.ts
web/i18n/lang/app-overview.en.ts
+1
-0
app-overview.zh.ts
web/i18n/lang/app-overview.zh.ts
+1
-0
common.en.ts
web/i18n/lang/common.en.ts
+1
-0
common.zh.ts
web/i18n/lang/common.zh.ts
+1
-0
common.ts
web/service/common.ts
+4
-0
format.ts
web/utils/format.ts
+20
-17
No files found.
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
View file @
e5e86fc0
import
React
from
'react'
import
React
from
'react'
import
{
EditKeyPopover
}
from
'./welcome-banner'
import
ChartView
from
'./chartView'
import
ChartView
from
'./chartView'
import
CardView
from
'./cardView'
import
CardView
from
'./cardView'
import
{
getLocaleOnServer
}
from
'@/i18n/server'
import
{
getLocaleOnServer
}
from
'@/i18n/server'
...
@@ -21,7 +20,6 @@ const Overview = async ({
...
@@ -21,7 +20,6 @@ const Overview = async ({
<
ApikeyInfoPanel
/>
<
ApikeyInfoPanel
/>
<
div
className=
'flex flex-row items-center justify-between mb-4 text-xl text-gray-900'
>
<
div
className=
'flex flex-row items-center justify-between mb-4 text-xl text-gray-900'
>
{
t
(
'overview.title'
)
}
{
t
(
'overview.title'
)
}
<
EditKeyPopover
/>
</
div
>
</
div
>
<
CardView
appId=
{
appId
}
/>
<
CardView
appId=
{
appId
}
/>
<
ChartView
appId=
{
appId
}
/>
<
ChartView
appId=
{
appId
}
/>
...
...
web/app/(commonLayout)/apps/Apps.tsx
View file @
e5e86fc0
...
@@ -4,12 +4,13 @@ import { useEffect, useRef } from 'react'
...
@@ -4,12 +4,13 @@ import { useEffect, useRef } from 'react'
import
useSWRInfinite
from
'swr/infinite'
import
useSWRInfinite
from
'swr/infinite'
import
{
debounce
}
from
'lodash-es'
import
{
debounce
}
from
'lodash-es'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
useSearchParams
}
from
'next/navigation'
import
AppCard
from
'./AppCard'
import
AppCard
from
'./AppCard'
import
NewAppCard
from
'./NewAppCard'
import
NewAppCard
from
'./NewAppCard'
import
type
{
AppListResponse
}
from
'@/models/app'
import
type
{
AppListResponse
}
from
'@/models/app'
import
{
fetchAppList
}
from
'@/service/apps'
import
{
fetchAppList
}
from
'@/service/apps'
import
{
useSelector
}
from
'@/context/app-context'
import
{
useSelector
}
from
'@/context/app-context'
import
{
NEED_REFRESH_APP_LIST_KEY
}
from
'@/config'
import
{
NEED_REFRESH_APP_LIST_KEY
,
SPARK_FREE_QUOTA_PENDING
}
from
'@/config'
const
getKey
=
(
pageIndex
:
number
,
previousPageData
:
AppListResponse
)
=>
{
const
getKey
=
(
pageIndex
:
number
,
previousPageData
:
AppListResponse
)
=>
{
if
(
!
pageIndex
||
previousPageData
.
has_more
)
if
(
!
pageIndex
||
previousPageData
.
has_more
)
...
@@ -23,6 +24,7 @@ const Apps = () => {
...
@@ -23,6 +24,7 @@ const Apps = () => {
const
loadingStateRef
=
useRef
(
false
)
const
loadingStateRef
=
useRef
(
false
)
const
pageContainerRef
=
useSelector
(
state
=>
state
.
pageContainerRef
)
const
pageContainerRef
=
useSelector
(
state
=>
state
.
pageContainerRef
)
const
anchorRef
=
useRef
<
HTMLAnchorElement
>
(
null
)
const
anchorRef
=
useRef
<
HTMLAnchorElement
>
(
null
)
const
searchParams
=
useSearchParams
()
useEffect
(()
=>
{
useEffect
(()
=>
{
document
.
title
=
`
${
t
(
'app.title'
)}
- Dify`
document
.
title
=
`
${
t
(
'app.title'
)}
- Dify`
...
@@ -30,6 +32,13 @@ const Apps = () => {
...
@@ -30,6 +32,13 @@ const Apps = () => {
localStorage
.
removeItem
(
NEED_REFRESH_APP_LIST_KEY
)
localStorage
.
removeItem
(
NEED_REFRESH_APP_LIST_KEY
)
mutate
()
mutate
()
}
}
if
(
localStorage
.
getItem
(
SPARK_FREE_QUOTA_PENDING
)
!==
'1'
&&
searchParams
.
get
(
'type'
)
===
'provider_apply_callback'
&&
searchParams
.
get
(
'provider'
)
===
'spark'
&&
searchParams
.
get
(
'result'
)
===
'success'
)
localStorage
.
setItem
(
SPARK_FREE_QUOTA_PENDING
,
'1'
)
},
[])
},
[])
useEffect
(()
=>
{
useEffect
(()
=>
{
...
...
web/app/components/app/overview/apikey-info-panel/index.tsx
View file @
e5e86fc0
...
@@ -3,18 +3,17 @@ import type { FC } from 'react'
...
@@ -3,18 +3,17 @@ import type { FC } from 'react'
import
React
,
{
useState
}
from
'react'
import
React
,
{
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
cn
from
'classnames'
import
cn
from
'classnames'
import
useSWR
from
'swr'
import
Progress
from
'./progress'
import
Progress
from
'./progress'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
{
LinkExternal02
,
XClose
}
from
'@/app/components/base/icons/src/vender/line/general'
import
{
LinkExternal02
,
XClose
}
from
'@/app/components/base/icons/src/vender/line/general'
import
AccountSetting
from
'@/app/components/header/account-setting'
import
AccountSetting
from
'@/app/components/header/account-setting'
import
{
fetchTenantInfo
}
from
'@/service/common'
import
{
IS_CE_EDITION
}
from
'@/config'
import
{
IS_CE_EDITION
}
from
'@/config'
import
{
useProviderContext
}
from
'@/context/provider-context'
import
{
useProviderContext
}
from
'@/context/provider-context'
import
{
formatNumber
}
from
'@/utils/format'
const
APIKeyInfoPanel
:
FC
=
()
=>
{
const
APIKeyInfoPanel
:
FC
=
()
=>
{
const
isCloud
=
!
IS_CE_EDITION
const
isCloud
=
!
IS_CE_EDITION
const
{
providers
}:
any
=
useProviderContext
()
const
{
textGenerationModelList
}
=
useProviderContext
()
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
...
@@ -22,37 +21,42 @@ const APIKeyInfoPanel: FC = () => {
...
@@ -22,37 +21,42 @@ const APIKeyInfoPanel: FC = () => {
const
[
isShow
,
setIsShow
]
=
useState
(
true
)
const
[
isShow
,
setIsShow
]
=
useState
(
true
)
const
{
data
:
userInfo
}
=
useSWR
({
url
:
'/info'
},
fetchTenantInfo
)
const
hasSetAPIKEY
=
!!
textGenerationModelList
?.
find
(({
model_provider
:
provider
})
=>
{
if
(
!
userInfo
)
if
(
provider
.
provider_type
===
'system'
&&
provider
.
quota_type
===
'paid'
)
return
null
return
true
if
(
provider
.
provider_type
===
'custom'
)
return
true
const
hasBindAPI
=
userInfo
?.
providers
?.
find
(({
token_is_set
})
=>
token_is_set
)
return
false
if
(
hasBindAPI
)
})
if
(
hasSetAPIKEY
)
return
null
return
null
// first show in trail and not used exhausted, else find the exhausted
// first show in trail and not used exhausted, else find the exhausted
const
[
used
,
total
,
providerName
]
=
(()
=>
{
const
[
used
,
total
,
unit
,
providerName
]
=
(()
=>
{
if
(
!
providers
||
!
isCloud
)
if
(
!
textGenerationModelList
||
!
isCloud
)
return
[
0
,
0
,
''
]
return
[
0
,
0
,
''
]
let
used
=
0
let
used
=
0
let
total
=
0
let
total
=
0
let
unit
=
'times'
let
trailProviderName
=
''
let
trailProviderName
=
''
let
hasFoundNotExhausted
=
false
let
hasFoundNotExhausted
=
false
Object
.
keys
(
providers
).
forEach
((
providerName
)
=>
{
textGenerationModelList
?.
filter
(({
model_provider
:
provider
})
=>
{
return
provider
.
quota_type
===
'trial'
}).
forEach
(({
model_provider
:
provider
})
=>
{
if
(
hasFoundNotExhausted
)
if
(
hasFoundNotExhausted
)
return
return
providers
[
providerName
].
providers
.
forEach
(({
quota_type
,
quota_limit
,
quota_used
}:
any
)
=>
{
const
{
provider_name
,
quota_used
,
quota_limit
,
quota_unit
}
=
provider
if
(
quota_type
===
'trial'
)
{
if
(
quota_limit
!==
quota_used
)
if
(
quota_limit
!==
quota_used
)
hasFoundNotExhausted
=
true
hasFoundNotExhausted
=
true
used
=
quota_used
total
=
quota_limit
used
=
quota_used
unit
=
quota_unit
total
=
quota_limit
trailProviderName
=
provider_name
trailProviderName
=
providerName
}
})
})
})
return
[
used
,
total
,
trailProviderName
]
return
[
used
,
total
,
unit
,
trailProviderName
]
})()
})()
const
usedPercent
=
Math
.
round
(
used
/
total
*
100
)
const
usedPercent
=
Math
.
round
(
used
/
total
*
100
)
const
exhausted
=
isCloud
&&
usedPercent
===
100
const
exhausted
=
isCloud
&&
usedPercent
===
100
...
@@ -81,9 +85,9 @@ const APIKeyInfoPanel: FC = () => {
...
@@ -81,9 +85,9 @@ const APIKeyInfoPanel: FC = () => {
{
isCloud
&&
(
{
isCloud
&&
(
<
div
className=
'my-5'
>
<
div
className=
'my-5'
>
<
div
className=
'flex items-center h-5 space-x-2 text-sm text-gray-700 font-medium'
>
<
div
className=
'flex items-center h-5 space-x-2 text-sm text-gray-700 font-medium'
>
<
div
>
{
t
(
'appOverview.apiKeyInfo.callTimes'
)
}
</
div
>
<
div
>
{
t
(
`appOverview.apiKeyInfo.${unit === 'times' ? 'callTimes' : 'usedToken'}`
)
}
</
div
>
<
div
>
·
</
div
>
<
div
>
·
</
div
>
<
div
className=
{
cn
(
'font-semibold'
,
exhausted
&&
'text-[#D92D20]'
)
}
>
{
used
}
/
{
total
}
</
div
>
<
div
className=
{
cn
(
'font-semibold'
,
exhausted
&&
'text-[#D92D20]'
)
}
>
{
formatNumber
(
used
)
}
/
{
formatNumber
(
total
)
}
</
div
>
</
div
>
</
div
>
<
Progress
className=
'mt-2'
value=
{
usedPercent
}
/>
<
Progress
className=
'mt-2'
value=
{
usedPercent
}
/>
</
div
>
</
div
>
...
...
web/app/components/header/account-setting/model-page/configs/spark.tsx
View file @
e5e86fc0
...
@@ -52,28 +52,28 @@ const config: ProviderConfig = {
...
@@ -52,28 +52,28 @@ const config: ProviderConfig = {
},
},
{
{
type
:
'text'
,
type
:
'text'
,
key
:
'api_
key
'
,
key
:
'api_
secret
'
,
required
:
true
,
required
:
true
,
label
:
{
label
:
{
'en'
:
'API
Key
'
,
'en'
:
'API
Secret
'
,
'zh-Hans'
:
'API
Key
'
,
'zh-Hans'
:
'API
Secret
'
,
},
},
placeholder
:
{
placeholder
:
{
'en'
:
'Enter your API
key
here'
,
'en'
:
'Enter your API
Secret
here'
,
'zh-Hans'
:
'在此输入您的 API
Key
'
,
'zh-Hans'
:
'在此输入您的 API
Secret
'
,
},
},
},
},
{
{
type
:
'text'
,
type
:
'text'
,
key
:
'api_
secret
'
,
key
:
'api_
key
'
,
required
:
true
,
required
:
true
,
label
:
{
label
:
{
'en'
:
'API
Secret
'
,
'en'
:
'API
Key
'
,
'zh-Hans'
:
'API
Secret
'
,
'zh-Hans'
:
'API
Key
'
,
},
},
placeholder
:
{
placeholder
:
{
'en'
:
'Enter your API
Secret
here'
,
'en'
:
'Enter your API
key
here'
,
'zh-Hans'
:
'在此输入您的 API
Secret
'
,
'zh-Hans'
:
'在此输入您的 API
Key
'
,
},
},
},
},
],
],
...
...
web/app/components/header/account-setting/model-page/declarations.ts
View file @
e5e86fc0
...
@@ -74,6 +74,10 @@ export type BackendModel = {
...
@@ -74,6 +74,10 @@ export type BackendModel = {
model_provider
:
{
model_provider
:
{
provider_name
:
ProviderEnum
provider_name
:
ProviderEnum
provider_type
:
PreferredProviderTypeEnum
provider_type
:
PreferredProviderTypeEnum
quota_type
:
'trial'
|
'paid'
quota_unit
:
'times'
|
'tokens'
quota_used
:
number
quota_limit
:
number
}
}
features
:
ModelFeature
[]
features
:
ModelFeature
[]
}
}
...
...
web/app/components/header/account-setting/model-page/model-item/FreeQuota.tsx
0 → 100644
View file @
e5e86fc0
import
{
useEffect
,
useState
}
from
'react'
import
type
{
FC
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useContext
}
from
'use-context-selector'
import
type
{
ProviderConfigItem
,
ProviderWithQuota
,
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
{
SPARK_FREE_QUOTA_PENDING
}
from
'@/config'
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 for free'
,
'zh-Hans'
:
'免费获取 300 万个 token'
,
},
}
const
FREE_QUOTA_TIP
=
{
'en'
:
'Your 3 million tokens will be credited in 5 minutes.'
,
'zh-Hans'
:
'您的 300 万 token 将在 5 分钟内到账。'
,
}
type
FreeQuotaProps
=
{
modelItem
:
ProviderConfigItem
onUpdate
:
()
=>
void
freeProvider
?:
ProviderWithQuota
}
const
FreeQuota
:
FC
<
FreeQuotaProps
>
=
({
modelItem
,
onUpdate
,
freeProvider
,
})
=>
{
const
{
locale
}
=
useContext
(
I18n
)
const
{
t
}
=
useTranslation
()
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
freeQuotaPending
,
setFreeQuotaPending
]
=
useState
(
false
)
useEffect
(()
=>
{
if
(
modelItem
.
key
===
ProviderEnumValue
.
spark
&&
localStorage
.
getItem
(
SPARK_FREE_QUOTA_PENDING
)
===
'1'
&&
freeProvider
&&
!
freeProvider
.
is_valid
)
setFreeQuotaPending
(
true
)
},
[
freeProvider
,
modelItem
.
key
])
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
)
}
}
if
(
freeQuotaPending
)
{
return
(
<
div
className=
'flex items-center'
>
⏳
<
div
className=
{
`${s.vender} ml-1 mr-2 text-xs font-medium text-transparent`
}
>
{
FREE_QUOTA_TIP
[
locale
]
}
</
div
>
<
Button
className=
'!px-3 !h-7 !rounded-md !text-xs !font-medium !bg-white !text-gray-700'
onClick=
{
onUpdate
}
>
{
t
(
'common.operation.reload'
)
}
</
Button
>
<
div
className=
'mx-2 w-[1px] h-4 bg-black/5'
/>
</
div
>
)
}
return
(
<
div
className=
'flex items-center'
>
📣
<
div
className=
{
`${s.vender} ml-1 mr-2 text-xs font-medium text-transparent`
}
>
{
TIP_MAP
[
modelItem
.
key
][
locale
]
}
</
div
>
<
Button
type=
'primary'
className=
'!px-3 !h-7 !rounded-md !text-xs !font-medium'
onClick=
{
handleClick
}
disabled=
{
loading
}
>
{
t
(
'common.operation.getForFree'
)
}
</
Button
>
<
div
className=
'mx-2 w-[1px] h-4 bg-black/5'
/>
</
div
>
)
}
export
default
FreeQuota
web/app/components/header/account-setting/model-page/model-item/Setting.tsx
View file @
e5e86fc0
...
@@ -2,8 +2,10 @@ import type { FC } from 'react'
...
@@ -2,8 +2,10 @@ import type { FC } from 'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
useContext
}
from
'use-context-selector'
import
{
useContext
}
from
'use-context-selector'
import
type
{
FormValue
,
Provider
,
ProviderConfigItem
,
ProviderWithConfig
,
ProviderWithQuota
}
from
'../declarations'
import
type
{
FormValue
,
Provider
,
ProviderConfigItem
,
ProviderWithConfig
,
ProviderWithQuota
}
from
'../declarations'
import
{
ProviderEnum
}
from
'../declarations'
import
Indicator
from
'../../../indicator'
import
Indicator
from
'../../../indicator'
import
Selector
from
'../selector'
import
Selector
from
'../selector'
import
FreeQuota
from
'./FreeQuota'
import
I18n
from
'@/context/i18n'
import
I18n
from
'@/context/i18n'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
{
IS_CE_EDITION
}
from
'@/config'
import
{
IS_CE_EDITION
}
from
'@/config'
...
@@ -13,6 +15,7 @@ type SettingProps = {
...
@@ -13,6 +15,7 @@ type SettingProps = {
modelItem
:
ProviderConfigItem
modelItem
:
ProviderConfigItem
onOpenModal
:
(
v
?:
FormValue
)
=>
void
onOpenModal
:
(
v
?:
FormValue
)
=>
void
onOperate
:
(
v
:
Record
<
string
,
any
>
)
=>
void
onOperate
:
(
v
:
Record
<
string
,
any
>
)
=>
void
onUpdate
:
()
=>
void
}
}
const
Setting
:
FC
<
SettingProps
>
=
({
const
Setting
:
FC
<
SettingProps
>
=
({
...
@@ -20,6 +23,7 @@ const Setting: FC<SettingProps> = ({
...
@@ -20,6 +23,7 @@ const Setting: FC<SettingProps> = ({
modelItem
,
modelItem
,
onOpenModal
,
onOpenModal
,
onOperate
,
onOperate
,
onUpdate
,
})
=>
{
})
=>
{
const
{
locale
}
=
useContext
(
I18n
)
const
{
locale
}
=
useContext
(
I18n
)
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
...
@@ -29,6 +33,15 @@ const Setting: FC<SettingProps> = ({
...
@@ -29,6 +33,15 @@ const Setting: FC<SettingProps> = ({
return
(
return
(
<
div
className=
'flex items-center'
>
<
div
className=
'flex items-center'
>
{
(
modelItem
.
key
===
ProviderEnum
.
minimax
||
modelItem
.
key
===
ProviderEnum
.
spark
)
&&
systemFree
&&
!
systemFree
?.
is_valid
&&
!
IS_CE_EDITION
&&
(
<
FreeQuota
modelItem=
{
modelItem
}
freeProvider=
{
systemFree
}
onUpdate=
{
onUpdate
}
/>
)
}
{
{
modelItem
.
disable
&&
!
IS_CE_EDITION
&&
(
modelItem
.
disable
&&
!
IS_CE_EDITION
&&
(
<
div
className=
'flex items-center text-xs text-gray-500'
>
<
div
className=
'flex items-center text-xs text-gray-500'
>
...
...
web/app/components/header/account-setting/model-page/model-item/index.tsx
View file @
e5e86fc0
...
@@ -26,6 +26,7 @@ const ModelItem: FC<ModelItemProps> = ({
...
@@ -26,6 +26,7 @@ const ModelItem: FC<ModelItemProps> = ({
modelItem
,
modelItem
,
onOpenModal
,
onOpenModal
,
onOperate
,
onOperate
,
onUpdate
,
})
=>
{
})
=>
{
const
{
locale
}
=
useContext
(
I18n
)
const
{
locale
}
=
useContext
(
I18n
)
const
custom
=
currentProvider
?.
providers
.
find
(
p
=>
p
.
provider_type
===
'custom'
)
as
ProviderWithModels
const
custom
=
currentProvider
?.
providers
.
find
(
p
=>
p
.
provider_type
===
'custom'
)
as
ProviderWithModels
...
@@ -47,6 +48,7 @@ const ModelItem: FC<ModelItemProps> = ({
...
@@ -47,6 +48,7 @@ const ModelItem: FC<ModelItemProps> = ({
modelItem=
{
modelItem
}
modelItem=
{
modelItem
}
onOpenModal=
{
onOpenModal
}
onOpenModal=
{
onOpenModal
}
onOperate=
{
onOperate
}
onOperate=
{
onOperate
}
onUpdate=
{
onUpdate
}
/>
/>
</
div
>
</
div
>
{
{
...
...
web/config/index.ts
View file @
e5e86fc0
...
@@ -120,3 +120,4 @@ export const VAR_ITEM_TEMPLATE = {
...
@@ -120,3 +120,4 @@ export const VAR_ITEM_TEMPLATE = {
export
const
appDefaultIconBackground
=
'#D5F5F6'
export
const
appDefaultIconBackground
=
'#D5F5F6'
export
const
NEED_REFRESH_APP_LIST_KEY
=
'needRefreshAppList'
export
const
NEED_REFRESH_APP_LIST_KEY
=
'needRefreshAppList'
export
const
SPARK_FREE_QUOTA_PENDING
=
'sparkFreeQuotaPending'
web/i18n/lang/app-overview.en.ts
View file @
e5e86fc0
...
@@ -23,6 +23,7 @@ const translation = {
...
@@ -23,6 +23,7 @@ const translation = {
},
},
},
},
callTimes
:
'Call times'
,
callTimes
:
'Call times'
,
usedToken
:
'Used token'
,
setAPIBtn
:
'Go to setup model provider'
,
setAPIBtn
:
'Go to setup model provider'
,
tryCloud
:
'Or try the cloud version of Dify with free quote'
,
tryCloud
:
'Or try the cloud version of Dify with free quote'
,
},
},
...
...
web/i18n/lang/app-overview.zh.ts
View file @
e5e86fc0
...
@@ -23,6 +23,7 @@ const translation = {
...
@@ -23,6 +23,7 @@ const translation = {
},
},
},
},
callTimes
:
'调用次数'
,
callTimes
:
'调用次数'
,
usedToken
:
'使用 Tokens'
,
setAPIBtn
:
'设置模型提供商'
,
setAPIBtn
:
'设置模型提供商'
,
tryCloud
:
'或者尝试使用 Dify 的云版本并使用试用配额'
,
tryCloud
:
'或者尝试使用 Dify 的云版本并使用试用配额'
,
},
},
...
...
web/i18n/lang/common.en.ts
View file @
e5e86fc0
...
@@ -25,6 +25,7 @@ const translation = {
...
@@ -25,6 +25,7 @@ const translation = {
download
:
'Download'
,
download
:
'Download'
,
setup
:
'Setup'
,
setup
:
'Setup'
,
getForFree
:
'Get for free'
,
getForFree
:
'Get for free'
,
reload
:
'Reload'
,
},
},
placeholder
:
{
placeholder
:
{
input
:
'Please enter'
,
input
:
'Please enter'
,
...
...
web/i18n/lang/common.zh.ts
View file @
e5e86fc0
...
@@ -25,6 +25,7 @@ const translation = {
...
@@ -25,6 +25,7 @@ const translation = {
download
:
'下载'
,
download
:
'下载'
,
setup
:
'设置'
,
setup
:
'设置'
,
getForFree
:
'免费获取'
,
getForFree
:
'免费获取'
,
reload
:
'刷新'
,
},
},
placeholder
:
{
placeholder
:
{
input
:
'请输入'
,
input
:
'请输入'
,
...
...
web/service/common.ts
View file @
e5e86fc0
...
@@ -169,3 +169,7 @@ export const fetchDefaultModal: Fetcher<BackendModel, string> = (url) => {
...
@@ -169,3 +169,7 @@ export const fetchDefaultModal: Fetcher<BackendModel, string> = (url) => {
export
const
updateDefaultModel
:
Fetcher
<
CommonResponse
,
{
url
:
string
;
body
:
any
}
>
=
({
url
,
body
})
=>
{
export
const
updateDefaultModel
:
Fetcher
<
CommonResponse
,
{
url
:
string
;
body
:
any
}
>
=
({
url
,
body
})
=>
{
return
post
(
url
,
{
body
})
as
Promise
<
CommonResponse
>
return
post
(
url
,
{
body
})
as
Promise
<
CommonResponse
>
}
}
export
const
submitFreeQuota
:
Fetcher
<
{
type
:
string
;
redirect_url
?:
string
;
result
?:
string
},
string
>
=
(
url
)
=>
{
return
post
(
url
)
as
Promise
<
{
type
:
string
;
redirect_url
?:
string
;
result
?:
string
}
>
}
web/utils/format.ts
View file @
e5e86fc0
/*
/*
* Formats a number with comma separators.
* Formats a number with comma separators.
formatNumber(1234567) will return '1,234,567'
formatNumber(1234567) will return '1,234,567'
formatNumber(1234567.89) will return '1,234,567.89'
formatNumber(1234567.89) will return '1,234,567.89'
*/
*/
export
const
formatNumber
=
(
num
:
number
|
string
)
=>
{
export
const
formatNumber
=
(
num
:
number
|
string
)
=>
{
if
(
!
num
)
return
num
;
if
(
!
num
)
let
parts
=
num
.
toString
().
split
(
"."
);
return
num
parts
[
0
]
=
parts
[
0
].
replace
(
/
\B(?=(\d{3})
+
(?!\d))
/g
,
","
);
const
parts
=
num
.
toString
().
split
(
'.'
)
return
parts
.
join
(
"."
);
parts
[
0
]
=
parts
[
0
].
replace
(
/
\B(?=(\d{3})
+
(?!\d))
/g
,
','
)
return
parts
.
join
(
'.'
)
}
}
export
const
formatFileSize
=
(
num
:
number
)
=>
{
export
const
formatFileSize
=
(
num
:
number
)
=>
{
if
(
!
num
)
return
num
;
if
(
!
num
)
const
units
=
[
''
,
'K'
,
'M'
,
'G'
,
'T'
,
'P'
];
return
num
let
index
=
0
;
const
units
=
[
''
,
'K'
,
'M'
,
'G'
,
'T'
,
'P'
]
let
index
=
0
while
(
num
>=
1024
&&
index
<
units
.
length
)
{
while
(
num
>=
1024
&&
index
<
units
.
length
)
{
num
=
num
/
1024
;
num
=
num
/
1024
index
++
;
index
++
}
}
return
num
.
toFixed
(
2
)
+
`
${
units
[
index
]}
B`
;
return
`
${
num
.
toFixed
(
2
)}${
units
[
index
]}
B`
}
}
export
const
formatTime
=
(
num
:
number
)
=>
{
export
const
formatTime
=
(
num
:
number
)
=>
{
if
(
!
num
)
return
num
;
if
(
!
num
)
const
units
=
[
'sec'
,
'min'
,
'h'
];
return
num
let
index
=
0
;
const
units
=
[
'sec'
,
'min'
,
'h'
]
let
index
=
0
while
(
num
>=
60
&&
index
<
units
.
length
)
{
while
(
num
>=
60
&&
index
<
units
.
length
)
{
num
=
num
/
60
;
num
=
num
/
60
index
++
;
index
++
}
}
return
`
${
num
.
toFixed
(
2
)}
${
units
[
index
]}
`
;
return
`
${
num
.
toFixed
(
2
)}
${
units
[
index
]}
`
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment