diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIChatMessage.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIChatMessage.tsx index 52d53d41d..c6e5b13ba 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIChatMessage.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/AIChatMessage.tsx @@ -129,16 +129,14 @@ export const AIChatMessage: React.FC = React.memo( Copy - {message.metadata && ( - - - - )} + + + )}
diff --git a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/TokenUsagePill.tsx b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/TokenUsagePill.tsx index 587081684..4a382e070 100644 --- a/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/TokenUsagePill.tsx +++ b/apps/desktop/layer/renderer/src/modules/ai-chat/components/message/TokenUsagePill.tsx @@ -8,6 +8,7 @@ import { isFreeRole } from "@follow/constants" import { useUserRole } from "@follow/store/user/hooks" import type { BizUIMetadata } from "@folo-services/ai-tools" import * as React from "react" +import { useTranslation } from "react-i18next" import { formatTokenCountString } from "~/modules/settings/tabs/ai/usage/utils" @@ -22,10 +23,51 @@ const formatDuration = (ms: number): string => { return `${ms}ms` } -export const TokenUsagePill: React.FC = ({ metadata, children }) => { - const userRole = useUserRole() - if (!metadata) return null - const isFreeUser = userRole ? isFreeRole(userRole) : false +interface ModelInfoSectionProps { + metadata: BizUIMetadata +} + +const ModelInfoSection: React.FC = ({ metadata }) => { + const { t } = useTranslation("ai") + const hasProviderInfo = metadata.provider != null || metadata.providerType != null + + return ( + <> +
+
{t("token_usage_pill.model_info")}
+
+ {metadata.modelUsed ?? t("token_usage_pill.unknown")} +
+
+ {hasProviderInfo && ( +
+
{t("token_usage_pill.provider_info")}
+
+ {metadata.provider ?? t("token_usage_pill.unknown")} + {metadata.providerType && ( + + ( + + {metadata.providerType === "byok" + ? t("token_usage_pill.byok") + : t("token_usage_pill.system")} + + ) + + )} +
+
+ )} + + ) +} + +interface FreeUserTokenUsageProps { + metadata: BizUIMetadata +} + +const FreeUserTokenUsage: React.FC = ({ metadata }) => { + const { t } = useTranslation("ai") const summarizedTokens = metadata.billedTokens ?? metadata.totalTokens ?? @@ -33,76 +75,93 @@ export const TokenUsagePill: React.FC = ({ metadata, childr metadata.contextTokens ?? null + return ( + <> + +
+
{t("token_usage_pill.credits_usage")}
+
+ {t("token_usage_pill.credits")}: + + {summarizedTokens != null ? formatTokenCountString(summarizedTokens) : "—"} + +
+
+ + ) +} + +interface NormalUserTokenUsageProps { + metadata: BizUIMetadata +} + +const NormalUserTokenUsage: React.FC = ({ metadata }) => { + const { t } = useTranslation("ai") const hasBillingMultiplier = metadata.billingMultiplier != null && metadata.billingMultiplier !== 1 const hasDuration = metadata.duration != null + return ( + <> + +
+
{t("token_usage_pill.credits_usage")}
+
+ {metadata.totalTokens != null && ( +
+ {t("token_usage_pill.total")}: + + {formatTokenCountString(metadata.totalTokens)} + +
+ )} + {metadata.billedTokens != null && ( +
+ {t("token_usage_pill.billed")}: + {formatTokenCountString(metadata.billedTokens)} +
+ )} +
+ {(hasDuration || hasBillingMultiplier) && ( + <> +
+
+ {hasDuration && ( +
+ {t("token_usage_pill.duration")}: + {formatDuration(metadata.duration!)} +
+ )} + {hasBillingMultiplier && ( +
+ {t("token_usage_pill.multiplier")}: + {metadata.billingMultiplier!}× +
+ )} +
+ + )} +
+ + ) +} + +export const TokenUsagePill: React.FC = ({ metadata, children }) => { + const userRole = useUserRole() + if (!metadata) return null + + const isFreeUser = userRole ? isFreeRole(userRole) : false + return ( {children} -
-
Model Info
-
- {metadata.modelUsed ?? "Unknown"} -
-
-
-
AI Credits Usage
- {isFreeUser ? ( -
- Credits: - - {summarizedTokens != null ? formatTokenCountString(summarizedTokens) : "—"} - -
- ) : ( - <> -
- {metadata.totalTokens != null && ( -
- Total: - - {formatTokenCountString(metadata.totalTokens)} - -
- )} - {metadata.billedTokens != null && ( -
- Billed: - - {formatTokenCountString(metadata.billedTokens)} - -
- )} -
- {(hasDuration || hasBillingMultiplier) && ( - <> -
-
- {hasDuration && ( -
- Duration: - - {formatDuration(metadata.duration!)} - -
- )} - {hasBillingMultiplier && ( -
- Multiplier: - - {metadata.billingMultiplier!}× - -
- )} -
- - )} - - )} -
+ {isFreeUser ? ( + + ) : ( + + )}
diff --git a/locales/ai/en.json b/locales/ai/en.json index 5fe8196f6..460c53d5f 100644 --- a/locales/ai/en.json +++ b/locales/ai/en.json @@ -337,6 +337,17 @@ "token_usage.title": "AI Credits Usage", "token_usage.tokens_remaining": "credits remaining", "token_usage.tokens_used": "{{used}} / {{total}} credits used", + "token_usage_pill.billed": "Billed", + "token_usage_pill.byok": "BYOK", + "token_usage_pill.credits": "Credits", + "token_usage_pill.credits_usage": "AI Credits Usage", + "token_usage_pill.duration": "Duration", + "token_usage_pill.model_info": "Model Info", + "token_usage_pill.multiplier": "Multiplier", + "token_usage_pill.provider_info": "Provider Info", + "token_usage_pill.system": "System", + "token_usage_pill.total": "Total", + "token_usage_pill.unknown": "Unknown", "usage_analysis.active_session": "Active Session", "usage_analysis.current_usage": "Current Usage", "usage_analysis.detailed_description": "Comprehensive overview of your AI usage patterns and insights", diff --git a/locales/ai/ja.json b/locales/ai/ja.json index a510f3624..56c278fe8 100644 --- a/locales/ai/ja.json +++ b/locales/ai/ja.json @@ -283,6 +283,17 @@ "timeline_summary.generating": "タイムラインを要約しています...", "timeline_summary.heading": "タイムラインに新しい内容はありますか", "timeline_summary.options.include": "タイムライン要約でチャット", + "token_usage_pill.billed": "請求済み", + "token_usage_pill.byok": "BYOK", + "token_usage_pill.credits": "クレジット", + "token_usage_pill.credits_usage": "AI クレジット使用", + "token_usage_pill.duration": "持続時間", + "token_usage_pill.model_info": "モデル情報", + "token_usage_pill.multiplier": "倍数", + "token_usage_pill.provider_info": "プロバイダー情報", + "token_usage_pill.system": "システム", + "token_usage_pill.total": "合計", + "token_usage_pill.unknown": "不明", "usage_analysis.active_session": "アクティブセッション", "usage_analysis.current_usage": "現在の使用状況", "usage_analysis.detailed_description": "AI使用パターンとインサイトの包括的概要", diff --git a/locales/ai/zh-CN.json b/locales/ai/zh-CN.json index a3cb01e27..bbe3ec34f 100644 --- a/locales/ai/zh-CN.json +++ b/locales/ai/zh-CN.json @@ -330,6 +330,17 @@ "token_usage.title": "令牌使用情况", "token_usage.tokens_remaining": "剩余令牌", "token_usage.tokens_used": "已使用{{used}} / {{total}}令牌", + "token_usage_pill.billed": "计费", + "token_usage_pill.byok": "BYOK", + "token_usage_pill.credits": "额度", + "token_usage_pill.credits_usage": "AI 额度使用", + "token_usage_pill.duration": "持续时间", + "token_usage_pill.model_info": "模型信息", + "token_usage_pill.multiplier": "倍数", + "token_usage_pill.provider_info": "提供商信息", + "token_usage_pill.system": "系统", + "token_usage_pill.total": "总计", + "token_usage_pill.unknown": "未知", "usage_analysis.active_session": "活跃会话", "usage_analysis.current_usage": "当前使用情况", "usage_analysis.detailed_description": "全面了解您的 AI 使用模式和洞察", diff --git a/locales/ai/zh-TW.json b/locales/ai/zh-TW.json index 70c79eb68..2d64b12af 100644 --- a/locales/ai/zh-TW.json +++ b/locales/ai/zh-TW.json @@ -302,6 +302,17 @@ "token_usage.title": "令牌使用狀況", "token_usage.tokens_remaining": "剩餘令牌", "token_usage.tokens_used": "已使用 {{used}} / {{total}} 令牌", + "token_usage_pill.billed": "計費", + "token_usage_pill.byok": "BYOK", + "token_usage_pill.credits": "額度", + "token_usage_pill.credits_usage": "AI 額度使用", + "token_usage_pill.duration": "持續時間", + "token_usage_pill.model_info": "模型資訊", + "token_usage_pill.multiplier": "倍數", + "token_usage_pill.provider_info": "提供商資訊", + "token_usage_pill.system": "系統", + "token_usage_pill.total": "總計", + "token_usage_pill.unknown": "未知", "usage_analysis.active_session": "活躍工作階段", "usage_analysis.current_usage": "目前使用狀況", "usage_analysis.detailed_description": "全面瞭解您的 AI 使用模式和洞察", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12e1588d8..88c2050d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,8 +10,8 @@ catalogs: specifier: 0.3.92 version: 0.3.92 '@folo-services/ai-tools': - specifier: 0.2.63 - version: 0.2.63 + specifier: 0.2.65 + version: 0.2.65 tailwindcss-uikit-colors: specifier: 1.0.0 version: 1.0.0 @@ -782,7 +782,7 @@ importers: version: link:../../../../packages/internal/utils '@folo-services/ai-tools': specifier: 'catalog:' - version: 0.2.63(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3) + version: 0.2.65(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3) '@types/node': specifier: 24.10.0 version: 24.10.0 @@ -4516,8 +4516,8 @@ packages: '@follow-app/client-sdk@0.3.92': resolution: {integrity: sha512-lJNMXyt40vkGFSsLt83zCE/zDjh2numbgR4FgR5Qh8VCp5cExmy+LgYOgmi+q3N8jpWyHw/ipE3cX1k3SPP/mQ==} - '@folo-services/ai-tools@0.2.63': - resolution: {integrity: sha512-Iyyx97fL6nDUQtd9fl2MRaN1+95yPT3iVI9PbOMGJxH87WXRpfeAg0dwmGHGdGfyNyM78kh32AymKrdpV8looQ==} + '@folo-services/ai-tools@0.2.65': + resolution: {integrity: sha512-Z6sScc3Hl66Yr8vHRy/yzOoFPhqqhQi0M9xjlmXd+d+0U1PCCKLwNSF+5ORjFDt2PICyA8thE8HBwI/Rl9pWUg==} '@folo-services/constants@0.1.53': resolution: {integrity: sha512-zD6CCC8+Sjos7tdutH+cefv5K+s+7jhYSM/asKUBGDq0FgmpgHnl9apB5qldlH/6hHtFwf+9HxjBtcHcewsNzg==} @@ -21234,7 +21234,7 @@ snapshots: - svelte - vue - '@folo-services/ai-tools@0.2.63(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)': + '@folo-services/ai-tools@0.2.65(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8)(pg@8.16.3)': dependencies: '@ai-sdk/openai': 2.0.63(zod@4.1.12) '@folo-services/drizzle': 0.1.44(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(better-sqlite3@12.4.1)(expo-sqlite@15.2.12(expo@53.0.12(@babel/core@7.28.5)(@expo/metro-runtime@5.0.4(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0)))(bufferutil@4.0.9)(graphql@16.8.1)(react-native-webview@13.16.0(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.28.5)(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.0.0))(react@19.0.0))(hono@4.9.8)(kysely@0.28.8) @@ -23796,7 +23796,7 @@ snapshots: debug: 2.6.9 invariant: 2.2.4 metro: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) - metro-config: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + metro-config: 0.82.4(bufferutil@4.0.9) metro-core: 0.82.4 semver: 7.7.3 transitivePeerDependencies: @@ -31631,22 +31631,6 @@ snapshots: - supports-color - utf-8-validate - metro-config@0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): - dependencies: - connect: 3.7.0 - cosmiconfig: 5.2.1 - flow-enums-runtime: 0.0.6 - jest-validate: 29.7.0 - metro: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) - metro-cache: 0.82.4 - metro-core: 0.82.4 - metro-runtime: 0.82.4 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - optional: true - metro-core@0.82.4: dependencies: flow-enums-runtime: 0.0.6 @@ -31832,7 +31816,7 @@ snapshots: metro-babel-transformer: 0.82.4 metro-cache: 0.82.4 metro-cache-key: 0.82.4 - metro-config: 0.82.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + metro-config: 0.82.4(bufferutil@4.0.9) metro-core: 0.82.4 metro-file-map: 0.82.4 metro-resolver: 0.82.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7e61c1799..1705f7430 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,7 +9,7 @@ packages: catalog: "@follow-app/client-sdk": 0.3.92 - "@folo-services/ai-tools": 0.2.63 + "@folo-services/ai-tools": 0.2.65 tailwindcss-uikit-colors: 1.0.0 typescript: 5.9.3