From a67777b8e2c1f4b6baa351f9377cfe06c3cb7eda Mon Sep 17 00:00:00 2001 From: JzoNg Date: Mon, 18 Mar 2024 22:31:55 +0800 Subject: [PATCH] app overview --- .../[appId]/overview/chartView.tsx | 57 ++++++++++------ web/app/components/app/overview/appChart.tsx | 68 ++++++++++++++++++- .../share/text-generation/index.tsx | 29 +++++--- web/i18n/en-US/app-overview.ts | 4 ++ web/i18n/zh-Hans/app-overview.ts | 4 ++ web/models/app.ts | 4 ++ web/service/apps.ts | 6 +- web/utils/model-config.ts | 4 ++ 8 files changed, 143 insertions(+), 33 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx index aada392326..3073075cfa 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx @@ -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({ 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} /> -
- - -
-
- {isChatApp - ? ( - - ) - : ( - - )} - -
-
- - -
+ {!isWorkflow && ( +
+ + +
+ )} + {!isWorkflow && ( +
+ {isChatApp + ? ( + + ) + : ( + + )} + +
+ )} + {!isWorkflow && ( +
+ + +
+ )} + {isWorkflow && ( +
+ + +
+ )} + {isWorkflow && ( +
+ + +
+ )} ) } diff --git a/web/app/components/app/overview/appChart.tsx b/web/app/components/app/overview/appChart.tsx index 9eaf95d023..7fd316a34b 100644 --- a/web/app/components/app/overview/appChart.tsx +++ b/web/app/components/app/overview/appChart.tsx @@ -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 = { colorType: 'blue', showTokens: true, }, + workflowCosts: { + colorType: 'blue', + }, } const sum = (arr: number[]): number => { @@ -366,4 +369,65 @@ export const CostChart: FC = ({ id, period }) => { /> } +export const WorkflowMessagesChart: FC = ({ id, period }) => { + const { t } = useTranslation() + const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-conversations`, params: period.query }, getWorkflowDailyConversations) + if (!response) + return + const noDataFlag = !response.data || response.data.length === 0 + return +} + +export const WorkflowDailyTerminalsChart: FC = ({ id, period }) => { + const { t } = useTranslation() + + const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/daily-terminals`, id, params: period.query }, getAppDailyEndUsers) + if (!response) + return + const noDataFlag = !response.data || response.data.length === 0 + return +} + +export const WorkflowCostChart: FC = ({ id, period }) => { + const { t } = useTranslation() + + const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/token-costs`, params: period.query }, getAppTokenCosts) + if (!response) + return + const noDataFlag = !response.data || response.data.length === 0 + return +} + +export const AvgUserInteractions: FC = ({ id, period }) => { + const { t } = useTranslation() + const { data: response } = useSWR({ url: `/apps/${id}/workflow/statistics/average-app-interactions`, params: period.query }, getAppStatistics) + if (!response) + return + const noDataFlag = !response.data || response.data.length === 0 + return +} + export default Chart diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 9405ac7d7f..5249af810e 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -333,17 +333,23 @@ const TextGeneration: FC = ({ 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 = ({ ...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 diff --git a/web/i18n/en-US/app-overview.ts b/web/i18n/en-US/app-overview.ts index 8abb5c58ef..6f947da02f 100644 --- a/web/i18n/en-US/app-overview.ts +++ b/web/i18n/en-US/app-overview.ts @@ -121,6 +121,10 @@ const translation = { title: 'Avg. Session Interactions', explanation: 'Continuous user-AI communication count; for conversation-based apps.', }, + avgUserInteractions: { + title: 'Avg. User Interactions', + explanation: 'Reflects the daily usage frequency of users. This metric reflects user stickiness.', + }, userSatisfactionRate: { title: 'User Satisfaction Rate', explanation: 'The number of likes per 1,000 messages. This indicates the proportion of answers that users are highly satisfied with.', diff --git a/web/i18n/zh-Hans/app-overview.ts b/web/i18n/zh-Hans/app-overview.ts index 8efde563f5..c548399618 100644 --- a/web/i18n/zh-Hans/app-overview.ts +++ b/web/i18n/zh-Hans/app-overview.ts @@ -121,6 +121,10 @@ const translation = { title: '平均会话互动数', explanation: '反应每个会话用户的持续沟通次数,如果用户与 AI 问答了 10 轮,即为 10。该指标反映了用户粘性。仅在对话型应用提供。', }, + avgUserInteractions: { + title: '平均用户调用次数', + explanation: '反应每天用户的使用次数。该指标反映了用户粘性。', + }, userSatisfactionRate: { title: '用户满意度', explanation: '每 1000 条消息的点赞数。反应了用户对回答十分满意的比例。', diff --git a/web/models/app.ts b/web/models/app.ts index f8126f4777..ac8f502fd2 100644 --- a/web/models/app.ts +++ b/web/models/app.ts @@ -79,6 +79,10 @@ export type AppDailyConversationsResponse = { data: Array<{ date: string; conversation_count: number }> } +export type WorkflowDailyConversationsResponse = { + data: Array<{ date: string; runs: number }> +} + export type AppStatisticsResponse = { data: Array<{ date: string }> } diff --git a/web/service/apps.ts b/web/service/apps.ts index 07c0fe7f9a..75f6792346 100644 --- a/web/service/apps.ts +++ b/web/service/apps.ts @@ -1,6 +1,6 @@ import type { Fetcher } from 'swr' import { del, get, post, put } from './base' -import type { ApikeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, GenerationIntroductionResponse, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse } from '@/models/app' +import type { ApikeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, GenerationIntroductionResponse, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WorkflowDailyConversationsResponse } from '@/models/app' import type { CommonResponse } from '@/models/common' import type { AppMode, ModelConfig } from '@/types/app' @@ -69,6 +69,10 @@ export const getAppDailyConversations: Fetcher(url, { params }) } +export const getWorkflowDailyConversations: Fetcher }> = ({ url, params }) => { + return get(url, { params }) +} + export const getAppStatistics: Fetcher }> = ({ url, params }) => { return get(url, { params }) } diff --git a/web/utils/model-config.ts b/web/utils/model-config.ts index b54b5f284e..20fad0f32f 100644 --- a/web/utils/model-config.ts +++ b/web/utils/model-config.ts @@ -1,6 +1,10 @@ import type { UserInputFormItem } from '@/types/app' import type { PromptVariable } from '@/models/debug' +export const workflowUserInputsToPromptVariables = () => { + // TODO +} + export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null, dataset_query_variable?: string) => { if (!useInputs) return []