Skip to content

feat(dashboard): 增加分流图,查看token流量。add traffic flow sankey chart - #5465

Merged
Calcium-Ion merged 3 commits into
QuantumNous:mainfrom
Quaternijkon:feat/dashboard-flow-sankey-controls
Jun 20, 2026
Merged

feat(dashboard): 增加分流图,查看token流量。add traffic flow sankey chart#5465
Calcium-Ion merged 3 commits into
QuantumNous:mainfrom
Quaternijkon:feat/dashboard-flow-sankey-controls

Conversation

@Quaternijkon

@Quaternijkon Quaternijkon commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

本 PR 为新版 Dashboard 增加分流图视图,用于按用户、API 密钥、模型和渠道查看调用流向与用量分布。与原先的图表风格一致,交互逻辑一致,筛选逻辑一致,统计分析方案一致。

后端新增 /api/data/flow/api/data/flow/self 数据接口,从消费日志中按用户、密钥、模型、渠道聚合额度、请求数、输入/输出 token、缓存读写 token 等指标。前端新增基于 VChart Sankey 的分流图,将这些聚合数据绘制为固定顺序的流向层级:

用户 -> API 密钥 -> 模型 -> 渠道

其中用户层固定显示,API 密钥层可通过 API Key 按钮显示或隐藏,模型与渠道至少显示一个;当模型和渠道同时显示时,模型始终位于渠道前面。

实现上,分流图复用 Dashboard 现有 VChart 配色体系,避免引入独立色板;节点颜色按完整层级稳定分配,切换 API 密钥层时不会导致模型或渠道颜色跳变。连线继承起点节点颜色,并根据同一 source 下的流量大小调整透明度,使细流线更突出、粗流线更克制。Tooltip 区分节点与连线:节点展示端点聚合统计,连线只展示当前 source-target 的聚合数据,避免 hover 时高亮或展示无关流线。

同时补充分流图相关测试,覆盖筛选、层级顺序、API 密钥层隐藏、颜色稳定性、tooltip 数据、错误态和空态等行为。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

#5435

✅ 提交前检查项 / Checklist

📸 运行证明 / Proof of Work

image image image image image

Summary by CodeRabbit

  • New Features
    • Added a Flow dashboard section with interactive Sankey charts for quota/tokens/requests, with stage show/hide and admin user filtering.
    • Added new backend endpoints to fetch flow data for admin and self, including time-range validation.
  • UI Improvements
    • Enhanced multi-select controls with an optional custom summary rendering mode.
  • Internationalization
    • Added Flow-related translations and empty-state/help text (EN/FR/JA/RU/VI/ZH).
  • Tests
    • Expanded API/model and dashboard unit tests to cover flow aggregation, role-based dimensions, and time-range validation.

Copilot AI review requested due to automatic review settings June 12, 2026 16:07
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8558e9f-f688-4568-91b0-77b33981e07b

📥 Commits

Reviewing files that changed from the base of the PR and between 3619b01 and e1a1cc4.

📒 Files selected for processing (24)
  • controller/usedata.go
  • controller/usedata_flow_test.go
  • model/log.go
  • model/task_cas_test.go
  • model/usedata.go
  • model/usedata_flow.go
  • model/usedata_flow_test.go
  • router/api-router.go
  • web/default/src/components/multi-select.tsx
  • web/default/src/features/dashboard/components/flow/flow-charts.tsx
  • web/default/src/features/dashboard/lib/charts.ts
  • web/default/src/features/dashboard/lib/flow-selection.test.ts
  • web/default/src/features/dashboard/lib/flow-selection.ts
  • web/default/src/features/dashboard/lib/flow.test.ts
  • web/default/src/features/dashboard/lib/flow.ts
  • web/default/src/features/dashboard/lib/index.ts
  • web/default/src/features/dashboard/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/static-keys.ts

Walkthrough

Adds a new "Flow" dashboard with complete backend-to-frontend flow quota visualization: centralizes quota logging around structured parameters, expands quota data with token/channel/node dimensions, implements role-specific aggregation APIs with batch name resolution, builds a Sankey graph transformation library with filtering and color palettes, integrates an interactive React component with user and metric controls, and provides multilingual i18n support across six locales.

Changes

Flow Dashboard Feature

Layer / File(s) Summary
Quota data model and logging refactoring
model/usedata.go, model/log.go, model/task_cas_test.go
QuotaData now tracks use_group, token_id, channel_id, node_name; LogQuotaData accepts QuotaDataLogParams struct; SaveQuotaDataCache keys upserts on expanded fields; RecordConsumeLog and RecordTaskBillingLog wire quota export with reused timestamps and structured parameters.
Backend flow quota API
model/usedata_flow.go, model/usedata_flow_test.go, controller/usedata.go, controller/usedata_flow_test.go, router/api-router.go
FlowQuotaData aggregates log rows with role-specific dimensions and batch-resolves token/channel names; GetFlowQuotaData dispatches to self/admin/root helpers; GetAllFlowQuotaDates and GetUserFlowQuotaDates validate time ranges and enforce role-based filtering; endpoints registered at /api/data/flow and /api/data/flow/self.
Frontend flow types and API client
web/default/src/features/dashboard/types.ts, web/default/src/features/dashboard/api.ts
FlowQuotaDataItem, FlowMetric, FlowRole, FlowNodeKind, graph data types (DashboardFlowNode, DashboardFlowLink, DashboardFlowGraph), filter/summary wrappers (FlowFilterOptions, ProcessedFlowData); getFlowQuotaDates API function dispatches between admin and self endpoints.
Frontend flow transformation library
web/default/src/features/dashboard/lib/flow.ts, web/default/src/features/dashboard/lib/flow.test.ts, web/default/src/features/dashboard/lib/index.ts
buildDashboardFlowData computes flow graphs with role-specific stages, user filtering, and metric aggregation; buildFlowFilterOptions assigns deterministic per-user colors; buildFlowSankeySpec generates full VChart Sankey config with link styling, tooltips, and interactive states; comprehensive tests validate path construction, filtering, stage visibility, and Sankey output.
Frontend flow selection utilities
web/default/src/features/dashboard/lib/flow-selection.ts, web/default/src/features/dashboard/lib/flow-selection.test.ts
requireSuccessfulFlowRows validates and extracts API response data; flowDisplayState derives chart mode (loading/error/empty/chart) with state precedence rules; compactFlowSelectionLabel formats user counts; visibleFlowUsers filters by selection; tests verify immutability, validation, and state ordering.
Frontend FlowCharts component and dashboard integration
web/default/src/features/dashboard/components/flow/flow-charts.tsx, web/default/src/features/dashboard/index.tsx, web/default/src/features/dashboard/section-registry.tsx, web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
FlowCharts renders metric selection tabs, stage visibility toggles with tooltips, admin user multi-select, and conditional chart/skeleton/error rendering; dashboard lazily loads flow section and reuses ModelsFilter with customizable title/description i18n keys; registry adds flow as a non-admin section.
Supporting components and internationalization
web/default/src/components/multi-select.tsx, web/default/src/features/dashboard/lib/charts.ts, web/default/src/i18n/locales/*.json, web/default/src/i18n/static-keys.ts
MultiSelect accepts optional renderSelectedSummary prop for compact display; getDashboardChartColors exported to filter palette strings; 100+ new translation keys added across English, French, Japanese, Russian, Vietnamese, and Chinese for flow UI, empty states, stage labels, and request attribution; static i18n keys updated for flow section and stage nodes.

Sequence Diagram

sequenceDiagram
  participant Browser
  participant FlowCharts
  participant DashboardAPI as getFlowQuotaDates
  participant Backend as controller/usedata
  participant GetFlowQuota as model.GetFlowQuotaData
  participant LOG_DB as quota_data table
  participant Lookup as token/channel resolution
  Browser->>FlowCharts: user selects time range & filters
  FlowCharts->>DashboardAPI: GET /api/data/flow or /flow/self
  DashboardAPI->>Backend: handler forwards start/end/username/userID
  Backend->>GetFlowQuota: (startTime, endTime, username, userID, role)
  GetFlowQuota->>LOG_DB: aggregate with role-specific GROUP BY
  LOG_DB-->>GetFlowQuota: grouped quota rows
  GetFlowQuota->>Lookup: batch load token and channel names
  Lookup-->>GetFlowQuota: TokenName and ChannelName populated
  GetFlowQuota-->>Backend: FlowQuotaData[]
  Backend-->>DashboardAPI: {success: true, data: [...]}
  DashboardAPI-->>FlowCharts: FlowQuotaDataItem[]
  FlowCharts->>FlowCharts: buildDashboardFlowData + buildFlowSankeySpec
  FlowCharts->>Browser: render Sankey chart with interactions
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • QuantumNous/new-api#1341: Refactors LogQuotaData call signature and parameter structure in RecordConsumeLog—this PR further evolves that pattern by introducing QuotaDataLogParams struct and expanding quota dimensions.

Poem

🐇 I hopped through the logs and found a delight,
Tokens and channels in aggregate light,
Sankey flows shimmer with colors so bold,
User-grouped stories in ribbons of gold,
Flow dashboard dreams in the dashboard so bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main feature: adding a traffic flow Sankey chart to the dashboard for viewing token flow. It accurately summarizes the primary change across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Quaternijkon Quaternijkon changed the title feat(dashboard): add traffic flow sankey chart feat(dashboard): 增加分流图,查看token流量。add traffic flow sankey chart Jun 12, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a new “Flow” dashboard section that visualizes traffic usage as a Sankey diagram and introduces backend + frontend plumbing to fetch and aggregate flow quota data.

Changes:

  • Add Flow usage aggregation to the API (/api/data/flow, /api/data/flow/self) and SQL-side token/cache computations.
  • Add dashboard flow processing + Sankey spec builder utilities with tests.
  • Add Flow UI section (lazy-loaded) plus new i18n keys and MultiSelect “summary chip” support.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web/default/src/i18n/static-keys.ts Adds Flow-related static translation keys referenced by the dashboard UI.
web/default/src/i18n/locales/{en,zh,vi,ru,ja,fr}.json Adds translations for Flow UI labels and empty/filter states.
web/default/src/features/dashboard/types.ts Introduces Flow-specific types (raw rows, graph nodes/links, filters, summaries).
web/default/src/features/dashboard/section-registry.tsx Registers the new flow dashboard section.
web/default/src/features/dashboard/lib/index.ts Exports flow builders/spec helpers from the dashboard lib barrel.
web/default/src/features/dashboard/lib/flow.ts Implements flow aggregation, filtering, coloring, and Sankey spec building.
web/default/src/features/dashboard/lib/flow.test.ts Adds unit tests for Flow data building and Sankey spec generation.
web/default/src/features/dashboard/lib/flow-selection.ts Adds selection + display-state helpers for Flow UI.
web/default/src/features/dashboard/lib/flow-selection.test.ts Adds unit tests for Flow selection helpers.
web/default/src/features/dashboard/lib/charts.ts Exposes a shared getDashboardChartColors palette helper for consistent chart colors.
web/default/src/features/dashboard/index.tsx Adds Flow section routing, actions, and lazy-loaded Flow charts.
web/default/src/features/dashboard/components/models/models-filter-dialog.tsx Makes filter dialog title/description configurable for reuse by Flow.
web/default/src/features/dashboard/components/flow/flow-charts.tsx Adds the Flow charts UI (controls, Sankey render, empty/error states).
web/default/src/features/dashboard/api.ts Adds a client API wrapper to fetch flow-quota rows.
web/default/src/components/multi-select.tsx Adds renderSelectedSummary to show a compact selection summary chip.
router/api-router.go Wires new Flow API routes into the Gin router.
model/usedata_flow.go Adds DB aggregation query for flow quota data and channel-name enrichment.
model/usedata_flow_test.go Adds model-level tests for aggregation, filtering, and cache token math.
controller/usedata.go Adds controller handlers for flow quota endpoints.
controller/usedata_flow_test.go Adds controller tests for admin username filtering and self scoping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +500 to +508
<VChart
key={`flow-${chartKey}`}
spec={{
...flowSpec,
theme: chartTheme,
background: 'transparent',
}}
option={VCHART_OPTION}
/>
Comment thread controller/usedata.go
Comment on lines +70 to +85
func GetAllFlowQuotaDates(c *gin.Context) {
startTimestamp, _ := strconv.ParseInt(c.Query("start_timestamp"), 10, 64)
endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
username := c.Query("username")
dates, err := model.GetFlowQuotaData(startTimestamp, endTimestamp, username, 0)
if err != nil {
common.ApiError(c, err)
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
"data": dates,
})
return
}
Comment thread controller/usedata.go
Comment on lines +91 to +97
if endTimestamp-startTimestamp > 2592000 {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "时间跨度不能超过 1 个月",
})
return
}
Comment thread model/usedata_flow.go Outdated
Comment on lines +78 to +96
func flowLogJSONValidGuard() string {
if common.UsingPostgreSQL {
return "COALESCE(logs.other, '') <> ''"
}
if common.UsingMySQL {
return "COALESCE(logs.other, '') <> '' AND JSON_VALID(logs.other)"
}
return "COALESCE(logs.other, '') <> '' AND json_valid(logs.other)"
}

func flowLogJSONNumberExpr(key string) string {
guard := flowLogJSONValidGuard()
if common.UsingPostgreSQL {
return fmt.Sprintf(
"(CASE WHEN %s THEN COALESCE(NULLIF(logs.other::jsonb ->> '%s', '')::integer, 0) ELSE 0 END)",
guard,
key,
)
}
Comment on lines +68 to +77
export async function getFlowQuotaDates(
params: {
start_timestamp: number
end_timestamp: number
default_time?: string
username?: string
},
isAdmin = false
) {
const endpoint = isAdmin ? '/api/data/flow' : '/api/data/flow/self'
Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests.
@Quaternijkon
Quaternijkon force-pushed the feat/dashboard-flow-sankey-controls branch from 0cc7252 to 3619b01 Compare June 12, 2026 16:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model/usedata_flow.go`:
- Around line 78-128: flowLogJSONValidGuard currently only checks non-empty for
PostgreSQL which lets malformed JSON reach the casts in flowLogJSONNumberExpr
and flowLogJSONStringExpr; change the PostgreSQL branch in flowLogJSONValidGuard
to a safe syntactic check (e.g. trim(logs.other) LIKE '{%' OR trim(logs.other)
LIKE '[%') combined with the non-empty check so invalid payloads are excluded
before any ::jsonb casts; the rest of the code (flowLogJSONNumberExpr and
flowLogJSONStringExpr) can continue using the returned guard variable.

In `@web/default/src/features/dashboard/components/flow/flow-charts.tsx`:
- Around line 334-345: The Sankey spec always receives formatQuota (in flowSpec
creation using buildFlowSankeySpec), causing Tokens/Requests views to use quota
formatting; update the flowSpec useMemo to select the formatter based on the
current metric (e.g., use formatQuota for 'quota', formatTokens for
'tokens'/'inputTokens'/'outputTokens', formatRequests for 'requests', etc.) and
pass that selected formatter into buildFlowSankeySpec instead of always passing
formatQuota so tooltips/values match the active metric.

In `@web/default/src/features/dashboard/lib/flow.test.ts`:
- Around line 1-9: The test file imports Node’s runner and assert; replace those
with Vitest imports so the suite runs with the frontend pipeline: change the
imports that reference "node:test" and "node:assert/strict" to import {
describe, test, expect } from "vitest" and update assertions to use expect(...)
instead of assert (locate usages in this file and update them); keep existing
type imports (FlowQuotaDataItem) and other module imports
(getDashboardChartColors, buildDashboardFlowData, buildFlowFilterOptions,
buildFlowSankeySpec) unchanged.

In `@web/default/src/features/dashboard/lib/flow.ts`:
- Around line 61-69: Import { t } from 'i18next' at the top of the module and
replace hardcoded English fallbacks in DEFAULT_FLOW_SANKEY_LABELS by wrapping
each value with t(), e.g. quota: t('Quota'), tokens: t('Tokens'), inputTokens:
t('Input Tokens'), etc.; do the same for the other default/fallback labels
object in this module (the fallback labels around lines 154-173) so all
non-React, user-visible default strings use i18n via t().
- Around line 888-914: Replace the hardcoded light-theme hex colors used in the
Sankey spec (the label style.fill '`#475569`' and the node stroke values:
node.style.stroke, node.state.hover.stroke, node.state.selected.stroke) with
values sourced from the app theme or CSS variables so the chart follows
dark/light mode; locate the style blocks in flow.ts (the label "style.fill" and
the "node" object) and pull colors from the shared theme/token provider (e.g.,
useTheme() or reading CSS vars like --color-slate-600 / --color-slate-800) or a
helper (e.g., colorAt theme lookup) and substitute those theme-derived values in
place of the hardcoded hex strings. Ensure the replacement preserves
opacity/alpha where needed (use rgba or CSS variable fallback) so hover/selected
states maintain intended contrast.
- Around line 447-458: The Sankey color mapping is being computed from the
filtered subset, causing chip colors (filterOptions.users[].color) to diverge;
change the flow color computation to always derive from the full dataset:
compute the palette/colors using flowColorMap(rows, palette) (or call
flowColorMap once with the unfiltered rows) and pass that color map/palette into
buildFlowGraph/buildDashboardFlowData so buildFlowGraph uses the full-data
colors instead of recomputing from filteredRows; update the places that
currently call flowColorMap or derive palette from filteredRows (e.g., the logic
around flowColorMap, palette, colors and where buildFlowGraph is invoked) to
accept and use the shared colors map so chips and the Sankey use the same stable
mapping.

In `@web/default/src/i18n/locales/ja.json`:
- Line 253: Update the Japanese translation for occurrences of the English
source "All API tokens" and any other entries whose source text is "API tokens"
to use "API トークン" instead of "API キー" to preserve the exact meaning; search the
locale JSON for the key/value pair containing "All API tokens" and replace the
value "すべての API キー" (and any other "API キー" translations tied to "API tokens")
with "すべての API トークン".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 23af550f-b7ce-4c36-89ef-966b529f8827

📥 Commits

Reviewing files that changed from the base of the PR and between 1507229 and 0cc7252.

📒 Files selected for processing (25)
  • controller/usedata.go
  • controller/usedata_flow_test.go
  • model/usedata_flow.go
  • model/usedata_flow_test.go
  • router/api-router.go
  • web/default/src/components/multi-select.tsx
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/flow/flow-charts.tsx
  • web/default/src/features/dashboard/components/models/models-filter-dialog.tsx
  • web/default/src/features/dashboard/index.tsx
  • web/default/src/features/dashboard/lib/charts.ts
  • web/default/src/features/dashboard/lib/flow-selection.test.ts
  • web/default/src/features/dashboard/lib/flow-selection.ts
  • web/default/src/features/dashboard/lib/flow.test.ts
  • web/default/src/features/dashboard/lib/flow.ts
  • web/default/src/features/dashboard/lib/index.ts
  • web/default/src/features/dashboard/section-registry.tsx
  • web/default/src/features/dashboard/types.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json
  • web/default/src/i18n/static-keys.ts

Comment thread model/usedata_flow.go Outdated
Comment thread web/default/src/features/dashboard/components/flow/flow-charts.tsx
Comment on lines +1 to +9
import assert from 'node:assert/strict'
import { describe, test } from 'node:test'
import type { FlowQuotaDataItem } from '../types'
import { getDashboardChartColors } from './charts'
import {
buildDashboardFlowData,
buildFlowFilterOptions,
buildFlowSankeySpec,
} from './flow'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use Vitest for this frontend suite instead of node:test.

This file sits under web/default/**/*.test.ts, but it imports Node’s test runner directly. That splits this suite away from the frontend’s expected runner and can leave it undiscovered or behaving differently from the rest of the dashboard tests. Please switch the imports to Vitest (describe, test, expect) so it runs under the same pipeline as the other frontend specs.

As per coding guidelines, web/default/**/*.test.ts: prioritize unit testing for utility functions and pure logic using Vitest with *.test.ts naming.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/lib/flow.test.ts` around lines 1 - 9, The
test file imports Node’s runner and assert; replace those with Vitest imports so
the suite runs with the frontend pipeline: change the imports that reference
"node:test" and "node:assert/strict" to import { describe, test, expect } from
"vitest" and update assertions to use expect(...) instead of assert (locate
usages in this file and update them); keep existing type imports
(FlowQuotaDataItem) and other module imports (getDashboardChartColors,
buildDashboardFlowData, buildFlowFilterOptions, buildFlowSankeySpec) unchanged.

Source: Coding guidelines

Comment on lines +61 to +69
const DEFAULT_FLOW_SANKEY_LABELS: FlowSankeyLabels = {
quota: 'Quota',
tokens: 'Tokens',
inputTokens: 'Input Tokens',
outputTokens: 'Output Tokens',
cacheRead: 'Cache Read',
cacheWrite: 'Cache Write',
requests: 'Requests',
share: 'Share',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate the fallback labels instead of hardcoding English here.

Quota, Tokens, Unknown User, Unknown Token, Unknown Model, and Unknown are all user-visible strings emitted from a non-React utility. They will stay English when data is missing or a caller uses the default labels. Import t from i18next in this module and wrap these fallbacks.

As per coding guidelines, web/default/**/*.ts: use import { t } from 'i18next' in non-React code for translations, and web/default/**/*.{tsx,ts}: all user-facing text must support i18n using t().

Also applies to: 154-173

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/lib/flow.ts` around lines 61 - 69, Import
{ t } from 'i18next' at the top of the module and replace hardcoded English
fallbacks in DEFAULT_FLOW_SANKEY_LABELS by wrapping each value with t(), e.g.
quota: t('Quota'), tokens: t('Tokens'), inputTokens: t('Input Tokens'), etc.; do
the same for the other default/fallback labels object in this module (the
fallback labels around lines 154-173) so all non-React, user-visible default
strings use i18n via t().

Source: Coding guidelines

Comment on lines +447 to +458
const colors = flowColorMap(rows, palette)

for (const row of rows) {
const metrics = rowMetrics(row)
const userID = userNodeId(row)
const tokenID = tokenNodeId(row)
const modelID = modelNodeId(row)
const channelID = channelNodeId(row)
const userColor = colors.get(userID) ?? colorAt(0, palette)
const tokenColor = colors.get(tokenID) ?? userColor
const modelColor = colors.get(modelID) ?? userColor
const channelColor = colors.get(channelID) ?? modelColor

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep color assignment anchored to the full dataset, not the filtered subset.

buildDashboardFlowData() builds filterOptions from rows, but it builds flow from filteredRows, and buildFlowGraph() derives its palette from that smaller set. As soon as a filter removes an earlier user/token/model/channel, the chart recomputes colors while filterOptions.users[].color stays on the original mapping, so the same entity can show one color in the chips and another in the Sankey. This also breaks stable downstream colors during filtering.

Suggested fix
-function buildFlowGraph(
+function buildFlowGraph(
   rows: FlowQuotaDataItem[],
   metric: FlowMetric,
   pathMode: FlowPathMode = DEFAULT_FLOW_PATH_MODE,
   includeTokenLayer = true,
-  palette?: readonly string[]
+  palette?: readonly string[],
+  colorRows: FlowQuotaDataItem[] = rows
 ): DashboardFlowGraph {
@@
-  const colors = flowColorMap(rows, palette)
+  const colors = flowColorMap(colorRows, palette)
@@
 export function buildDashboardFlowData(
   rows: FlowQuotaDataItem[],
   metric: FlowMetric = 'quota',
   options: FlowBuildOptions = {}
 ): ProcessedFlowData {
@@
     flow: buildFlowGraph(
       filteredRows,
       metric,
       pathMode,
       includeTokenLayer,
-      palette
+      palette,
+      rows
     ),

Also applies to: 687-702

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/lib/flow.ts` around lines 447 - 458, The
Sankey color mapping is being computed from the filtered subset, causing chip
colors (filterOptions.users[].color) to diverge; change the flow color
computation to always derive from the full dataset: compute the palette/colors
using flowColorMap(rows, palette) (or call flowColorMap once with the unfiltered
rows) and pass that color map/palette into buildFlowGraph/buildDashboardFlowData
so buildFlowGraph uses the full-data colors instead of recomputing from
filteredRows; update the places that currently call flowColorMap or derive
palette from filteredRows (e.g., the logic around flowColorMap, palette, colors
and where buildFlowGraph is invoked) to accept and use the shared colors map so
chips and the Sankey use the same stable mapping.

Comment on lines +888 to +914
style: {
fill: '#475569',
fontSize: 11,
fontWeight: 600,
},
},
node: {
interactive: true,
style: {
fill: (datum: Record<string, unknown>) =>
String(sankeyDatumValue(datum, 'color') ?? colorAt(0)),
fillOpacity: 0.92,
stroke: 'rgba(148, 163, 184, 0.45)',
lineWidth: 1,
cursor: 'pointer',
pickMode: 'accurate',
},
state: {
hover: {
fillOpacity: 1,
stroke: 'rgba(15, 23, 42, 0.68)',
lineWidth: 1.5,
},
selected: {
fillOpacity: 1,
stroke: 'rgba(15, 23, 42, 0.68)',
lineWidth: 1.5,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Avoid hardcoded light-theme colors in the Sankey spec.

The label fill and node strokes are fixed to light-theme slate values here, so the chart cannot follow the active dashboard theme. In dark mode this will drift from the rest of the dashboard and can reduce contrast on labels and outlines. Pull these from the theme/CSS variables instead of baking them into the spec.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/lib/flow.ts` around lines 888 - 914,
Replace the hardcoded light-theme hex colors used in the Sankey spec (the label
style.fill '`#475569`' and the node stroke values: node.style.stroke,
node.state.hover.stroke, node.state.selected.stroke) with values sourced from
the app theme or CSS variables so the chart follows dark/light mode; locate the
style blocks in flow.ts (the label "style.fill" and the "node" object) and pull
colors from the shared theme/token provider (e.g., useTheme() or reading CSS
vars like --color-slate-600 / --color-slate-800) or a helper (e.g., colorAt
theme lookup) and substitute those theme-derived values in place of the
hardcoded hex strings. Ensure the replacement preserves opacity/alpha where
needed (use rgba or CSS variable fallback) so hover/selected states maintain
intended contrast.

"Ali": "Ali",
"Alipay": "Alipay",
"All": "すべて",
"All API tokens": "すべての API キー",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

“API tokens” is mistranslated as “API keys”.

Line 253 and Line 2522 currently translate “API tokens” as API キー, which changes meaning and can confuse users in token-related views.

Suggested fix
-    "All API tokens": "すべての API キー",
+    "All API tokens": "すべての API トークン",
...
-    "No API tokens": "API キーなし",
+    "No API tokens": "API トークンなし",

Also applies to: 2522-2522

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/i18n/locales/ja.json` at line 253, Update the Japanese
translation for occurrences of the English source "All API tokens" and any other
entries whose source text is "API tokens" to use "API トークン" instead of "API キー"
to preserve the exact meaning; search the locale JSON for the key/value pair
containing "All API tokens" and replace the value "すべての API キー" (and any other
"API キー" translations tied to "API tokens") with "すべての API トークン".

@Calcium-Ion

Copy link
Copy Markdown
Member

Thanks for your PR! This feature is very useful. I will merge it after making some modifications.

@Calcium-Ion
Calcium-Ion merged commit a68041f into QuantumNous:main Jun 20, 2026
1 check was pending
ruanhangjian pushed a commit to ruanhangjian/new-api that referenced this pull request Jul 11, 2026
* feat(dashboard): add traffic flow sankey chart

Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests.

* feat: build flow chart from quota data

---------

Co-authored-by: CaIon <i@caion.me>
zhaodechao2008 pushed a commit to zhaodechao2008/new-api that referenced this pull request Jul 27, 2026
* feat(dashboard): add traffic flow sankey chart

Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests.

* feat: build flow chart from quota data

---------

Co-authored-by: CaIon <i@caion.me>
330079598 pushed a commit to 330079598/new-api that referenced this pull request Aug 19, 2026
* feat(dashboard): add traffic flow sankey chart

Add dashboard flow APIs and a Sankey-based flow view with user, optional API key, model, and channel layers.\n\nReuse the dashboard VChart palette, add precise link/node tooltips and interactions, and cover filtering, layer ordering, color stability, and error states with tests.

* feat: build flow chart from quota data

---------

Co-authored-by: CaIon <i@caion.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants