mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 09:58:04 +08:00
app overview
This commit is contained in:
@ -4,7 +4,7 @@ import dayjs from 'dayjs'
|
||||
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { PeriodParams } from '@/app/components/app/overview/appChart'
|
||||
import { AvgResponseTime, AvgSessionInteractions, ConversationsChart, CostChart, EndUsersChart, TokenPerSecond, UserSatisfactionRate } from '@/app/components/app/overview/appChart'
|
||||
import { AvgResponseTime, AvgSessionInteractions, AvgUserInteractions, ConversationsChart, CostChart, EndUsersChart, TokenPerSecond, UserSatisfactionRate, WorkflowCostChart, WorkflowDailyTerminalsChart, WorkflowMessagesChart } from '@/app/components/app/overview/appChart'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import { TIME_PERIOD_LIST } from '@/app/components/app/log/filter'
|
||||
@ -24,6 +24,7 @@ export default function ChartView({ appId }: IChartViewProps) {
|
||||
const { t } = useTranslation()
|
||||
const { appDetail } = useAppStore()
|
||||
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
|
||||
const isWorkflow = appDetail?.mode === 'workflow'
|
||||
const [period, setPeriod] = useState<PeriodParams>({ name: t('appLog.filter.period.last7days'), query: { start: today.subtract(7, 'day').format(queryDateFormat), end: today.format(queryDateFormat) } })
|
||||
|
||||
const onSelect = (item: Item) => {
|
||||
@ -54,24 +55,42 @@ export default function ChartView({ appId }: IChartViewProps) {
|
||||
defaultValue={7}
|
||||
/>
|
||||
</div>
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<ConversationsChart period={period} id={appId} />
|
||||
<EndUsersChart period={period} id={appId} />
|
||||
</div>
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
{isChatApp
|
||||
? (
|
||||
<AvgSessionInteractions period={period} id={appId} />
|
||||
)
|
||||
: (
|
||||
<AvgResponseTime period={period} id={appId} />
|
||||
)}
|
||||
<TokenPerSecond period={period} id={appId} />
|
||||
</div>
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<UserSatisfactionRate period={period} id={appId} />
|
||||
<CostChart period={period} id={appId} />
|
||||
</div>
|
||||
{!isWorkflow && (
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<ConversationsChart period={period} id={appId} />
|
||||
<EndUsersChart period={period} id={appId} />
|
||||
</div>
|
||||
)}
|
||||
{!isWorkflow && (
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
{isChatApp
|
||||
? (
|
||||
<AvgSessionInteractions period={period} id={appId} />
|
||||
)
|
||||
: (
|
||||
<AvgResponseTime period={period} id={appId} />
|
||||
)}
|
||||
<TokenPerSecond period={period} id={appId} />
|
||||
</div>
|
||||
)}
|
||||
{!isWorkflow && (
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<UserSatisfactionRate period={period} id={appId} />
|
||||
<CostChart period={period} id={appId} />
|
||||
</div>
|
||||
)}
|
||||
{isWorkflow && (
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<WorkflowMessagesChart period={period} id={appId} />
|
||||
<WorkflowDailyTerminalsChart period={period} id={appId} />
|
||||
</div>
|
||||
)}
|
||||
{isWorkflow && (
|
||||
<div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
|
||||
<WorkflowCostChart period={period} id={appId} />
|
||||
<AvgUserInteractions period={period} id={appId} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ import { formatNumber } from '@/utils/format'
|
||||
import Basic from '@/app/components/app-sidebar/basic'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import type { AppDailyConversationsResponse, AppDailyEndUsersResponse, AppTokenCostsResponse } from '@/models/app'
|
||||
import { getAppDailyConversations, getAppDailyEndUsers, getAppStatistics, getAppTokenCosts } from '@/service/apps'
|
||||
import { getAppDailyConversations, getAppDailyEndUsers, getAppStatistics, getAppTokenCosts, getWorkflowDailyConversations } from '@/service/apps'
|
||||
const valueFormatter = (v: string | number) => v
|
||||
|
||||
const COLOR_TYPE_MAP = {
|
||||
@ -36,7 +36,7 @@ const COMMON_COLOR_MAP = {
|
||||
}
|
||||
|
||||
type IColorType = 'green' | 'orange' | 'blue'
|
||||
type IChartType = 'conversations' | 'endUsers' | 'costs'
|
||||
type IChartType = 'conversations' | 'endUsers' | 'costs' | 'workflowCosts'
|
||||
type IChartConfigType = { colorType: IColorType; showTokens?: boolean }
|
||||
|
||||
const commonDateFormat = 'MMM D, YYYY'
|
||||
@ -52,6 +52,9 @@ const CHART_TYPE_CONFIG: Record<string, IChartConfigType> = {
|
||||
colorType: 'blue',
|
||||
showTokens: true,
|
||||
},
|
||||
workflowCosts: {
|
||||
colorType: 'blue',
|
||||
},
|
||||
}
|
||||
|
||||
const sum = (arr: number[]): number => {
|
||||
@ -366,4 +369,65 @@ export const CostChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
/>
|
||||
}
|
||||
|
||||
export const WorkflowMessagesChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const { t } = useTranslation()
|
||||
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-conversations`, params: period.query }, getWorkflowDailyConversations)
|
||||
if (!response)
|
||||
return <Loading />
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.totalMessages.title'), explanation: t('appOverview.analysis.totalMessages.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData({ ...(period.query ?? defaultPeriod), key: 'runs' }) }}
|
||||
chartType='conversations'
|
||||
valueKey='runs'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
}
|
||||
|
||||
export const WorkflowDailyTerminalsChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-terminals`, id, params: period.query }, getAppDailyEndUsers)
|
||||
if (!response)
|
||||
return <Loading />
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.activeUsers.title'), explanation: t('appOverview.analysis.activeUsers.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartType='endUsers'
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
}
|
||||
|
||||
export const WorkflowCostChart: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/token-costs`, params: period.query }, getAppTokenCosts)
|
||||
if (!response)
|
||||
return <Loading />
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.tokenUsage.title'), explanation: t('appOverview.analysis.tokenUsage.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData(period.query ?? defaultPeriod) }}
|
||||
chartType='workflowCosts'
|
||||
{...(noDataFlag && { yMax: 100 })}
|
||||
/>
|
||||
}
|
||||
|
||||
export const AvgUserInteractions: FC<IBizChartProps> = ({ id, period }) => {
|
||||
const { t } = useTranslation()
|
||||
const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/average-app-interactions`, params: period.query }, getAppStatistics)
|
||||
if (!response)
|
||||
return <Loading />
|
||||
const noDataFlag = !response.data || response.data.length === 0
|
||||
return <Chart
|
||||
basicInfo={{ title: t('appOverview.analysis.avgUserInteractions.title'), explanation: t('appOverview.analysis.avgUserInteractions.explanation'), timePeriod: period.name }}
|
||||
chartData={!noDataFlag ? response : { data: getDefaultChartData({ ...(period.query ?? defaultPeriod), key: 'interactions' }) } as any}
|
||||
chartType='conversations'
|
||||
valueKey='interactions'
|
||||
isAvg
|
||||
{...(noDataFlag && { yMax: 500 })}
|
||||
/>
|
||||
}
|
||||
|
||||
export default Chart
|
||||
|
||||
@ -333,17 +333,23 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
if (!isInstalledApp)
|
||||
await checkOrSetAccessToken()
|
||||
|
||||
return Promise.all([isInstalledApp
|
||||
? {
|
||||
app_id: installedAppInfo?.id,
|
||||
site: {
|
||||
title: installedAppInfo?.app.name,
|
||||
prompt_public: false,
|
||||
copyright: '',
|
||||
},
|
||||
plan: 'basic',
|
||||
}
|
||||
: fetchAppInfo(), fetchAppParams(isInstalledApp, installedAppInfo?.id), fetchSavedMessage()])
|
||||
return Promise.all([
|
||||
isInstalledApp
|
||||
? {
|
||||
app_id: installedAppInfo?.id,
|
||||
site: {
|
||||
title: installedAppInfo?.app.name,
|
||||
prompt_public: false,
|
||||
copyright: '',
|
||||
},
|
||||
plan: 'basic',
|
||||
}
|
||||
: fetchAppInfo(),
|
||||
fetchAppParams(isInstalledApp, installedAppInfo?.id),
|
||||
!isWorkflow
|
||||
? fetchSavedMessage()
|
||||
: {},
|
||||
])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -360,6 +366,7 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
...file_upload.image,
|
||||
image_file_size_limit: appParams?.system_parameters?.image_file_size_limit,
|
||||
})
|
||||
// ###TODO###
|
||||
const prompt_variables = userInputsFormToPromptVariables(user_input_form)
|
||||
setPromptConfig({
|
||||
prompt_template: '', // placeholder for feture
|
||||
|
||||
Reference in New Issue
Block a user