Skip to content

feat: performance chart - #6637

Draft
seefs001 wants to merge 2 commits into
QuantumNous:mainfrom
seefs001:feature/performance-chart
Draft

feat: performance chart#6637
seefs001 wants to merge 2 commits into
QuantumNous:mainfrom
seefs001:feature/performance-chart

Conversation

@seefs001

@seefs001 seefs001 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

⚠️ 提交说明 / PR Notice

Important

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

📝 变更描述 / Description

(简述:做了什么?为什么这样改能生效?请基于你对代码逻辑的理解来写,避免粘贴未经整理的内容)

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

Summary by CodeRabbit

  • New Features

    • Added an administrator model-performance dashboard with model and group breakdowns.
    • View requests, success rates, failures, latency, TTFT, throughput, token usage, active groups, and period-over-period changes.
    • Added health indicators with explanations for degraded, critical, incomplete, or unavailable data.
    • Supports sorting, filtering, expandable groups, responsive layouts, automatic refresh, and retry actions.
    • Added localized performance-monitoring messages across supported languages.
  • Bug Fixes

    • Improved performance metric collection and handling of time-range and incomplete-data scenarios.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds end-to-end administrative model performance metrics. The change includes backend aggregation and health classification, an authenticated API, a dashboard table with filtering and refresh behavior, validation tests, synchronization updates, and translations.

Changes

Administrative performance metrics

Layer / File(s) Summary
Backend query and API flow
controller/perf_metrics.go, controller/perf_metrics_admin_test.go, model/ability.go, model/perf_metric.go, model/perf_metric_admin_test.go, router/api-router.go
Adds timestamp validation, grouped half-open metric queries, available-range lookup, enabled-ability access, and the admin route.
Aggregation, health, and bucket synchronization
pkg/perf_metrics/admin.go, pkg/perf_metrics/admin_test.go, pkg/perf_metrics/flush.go, pkg/perf_metrics/metrics.go
Adds period alignment, persisted and hot-bucket aggregation, health states, period changes, and synchronized hot-bucket reads and writes.
Frontend contracts and data shaping
web/src/features/performance-metrics/types.ts, web/src/features/performance-metrics/api.ts, web/src/features/performance-metrics/lib/admin.ts, web/src/features/performance-metrics/lib/__tests__/admin.test.ts
Adds typed responses, API access, nested model/group rows, display states, health ranking, and helper tests.
Dashboard table and filtering
web/src/features/dashboard/components/models/model-performance-columns.tsx, web/src/features/dashboard/components/models/model-performance-table.tsx, web/src/features/dashboard/index.tsx, web/src/components/data-table/hooks/use-data-table.ts
Adds the model performance table, metric columns, filters, sorting, refresh behavior, responsive visibility, and dashboard integration.
Performance monitoring translations
web/src/i18n/locales/*.json
Adds translated labels and messages for health states, metrics, alerts, incomplete data, disabled metrics, and monitoring actions.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Admin as Admin dashboard
  participant API as Admin metrics API
  participant Query as QueryAdmin
  participant DB as Metric database
  participant Table as ModelPerformanceTable

  Admin->>API: Request selected timestamp range
  API->>Query: QueryAdmin(startTs, endTs)
  Query->>DB: Load persisted summaries and available range
  DB-->>Query: Metric buckets
  Query-->>API: Aggregated model and group results
  API-->>Table: Typed performance response
  Table->>Table: Build rows, health states, filters, and display states
Loading

Poem

A rabbit reviews each metric row,
With health badges in a tidy show.
Buckets align and filters hop,
Fresh data flows from bottom to top.
“No samples?” says Bun, “show an em dash!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 identifies the performance feature, but the changes primarily add administrative performance metrics and a model performance table.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 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.

@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: 2

🧹 Nitpick comments (5)
pkg/perf_metrics/admin.go (1)

292-300: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reduce the repeated full-map scans per model.

buildAdminModels calls countActiveAdminGroups twice and buildAdminGroups once for every model name. Each call scans the whole current or previous map. The total cost is O(models × model-group pairs).

Group the counters by model name once, then reuse that index in the per-model loop.

Also applies to: 302-313

🤖 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 `@pkg/perf_metrics/admin.go` around lines 292 - 300, Refactor buildAdminModels
and the related countActiveAdminGroups/buildAdminGroups flow to group current
and previous counters by model name once before the per-model loop. Reuse each
model’s grouped counters for counting active groups and building groups,
eliminating repeated full-map scans while preserving existing results.
model/perf_metric_admin_test.go (1)

10-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Initialize DB before using it in this package-level test.

model/perf_metric_admin_test.go uses DB on lines 13, 21, and 23 without local fixture setup. Package model already has a TestMain in another test file with AutoMigrate support; add the needed local setup there or use an explicit setup fixture so this test does not depend on another test file’s bootstrap.

🤖 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 `@model/perf_metric_admin_test.go` around lines 10 - 23, Initialize the
database explicitly before TestGetPerfMetricGroupSummariesUsesHalfOpenRange
accesses DB, using the package’s established TestMain or an equivalent local
fixture with AutoMigrate support. Ensure the test remains self-contained and
does not rely on bootstrap behavior from another test file.

Source: Coding guidelines

controller/perf_metrics_admin_test.go (1)

22-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case for a non-positive start_timestamp.

The handler rejects startTs <= 0 at line 88 of controller/perf_metrics.go. No table entry covers that branch.

♻️ Proposed additional case
 		{name: "reversed", query: "?start_timestamp=200&end_timestamp=100"},
+		{name: "non positive start", query: "?start_timestamp=0&end_timestamp=100"},
 		{name: "future end", query: "?start_timestamp=100&end_timestamp=" + strconv.FormatInt(now+60, 10)},
🤖 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 `@controller/perf_metrics_admin_test.go` around lines 22 - 27, Add a
table-driven test case in the existing perf metrics validation cases for a
non-positive start_timestamp, using a query that sets start_timestamp to zero or
a negative value and a valid end_timestamp. Assert the handler rejects it,
covering the startTs <= 0 branch in the perf metrics handler.
web/src/features/performance-metrics/lib/__tests__/admin.test.ts (1)

85-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test cases for the untested display-state branches.

getAdminPerformanceDisplayState has six branches: loading, error, disabled, no_complete_buckets, empty, and ready. The current tests exercise only error and disabled. Add cases for no_complete_buckets, empty, and ready to lock in the precedence order.

Also add a case for buildAdminPerformanceRows(undefined) to cover the early-return branch, and a case with more than one model to confirm multi-model mapping.

As per path instructions, web/**/__tests__/**/*.{test,spec}.{ts,tsx} requires that "测试应覆盖主要成功路径及变更涉及的关键边界和失败路径" (tests should cover the main success path plus key boundaries and failure paths introduced by the change).

🤖 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/src/features/performance-metrics/lib/__tests__/admin.test.ts` around
lines 85 - 118, Extend the tests in the admin model performance helpers suite to
cover getAdminPerformanceDisplayState results for no_complete_buckets, empty,
and ready, preserving the documented precedence among all six states. Add
buildAdminPerformanceRows(undefined) coverage for its early return and a
multi-model fixture asserting each model is mapped correctly, while retaining
the existing error and disabled cases.

Source: Path instructions

web/src/features/dashboard/components/models/model-performance-columns.tsx (1)

123-127: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize the columns array to avoid unnecessary re-creation.

useModelPerformanceColumns builds a new ColumnDef array on every call. ModelPerformanceTable calls this hook on every render, including the automatic refetch every REFRESH_INTERVAL_MS. The new array identity also defeats the useMemo on buildColumnSizingBounds(columns) inside useDataTable, since that memo is keyed on [columns].

Wrap the returned array in useMemo keyed on [t, locale] (or [i18n.language]) to keep column identity stable across renders.

♻️ Proposed refactor
 export function useModelPerformanceColumns(): ColumnDef<AdminPerformanceTableRow>[] {
   const { t, i18n } = useTranslation()
   const locale = toIntlLocale(i18n.resolvedLanguage || i18n.language)

-  return [
+  return useMemo<ColumnDef<AdminPerformanceTableRow>[]>(() => [
     {
       accessorKey: 'model_name',
       ...
     },
-  ]
+  ], [t, locale])
 }

Based on coding guidelines: "合理使用 useMemouseCallbackReact.memo,避免渲染路径中不必要的新对象或数组" (web/**/*.tsx).

🤖 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/src/features/dashboard/components/models/model-performance-columns.tsx`
around lines 123 - 127, Memoize the columns array returned by
useModelPerformanceColumns with useMemo, using [t, locale] as dependencies so it
is recreated only when translations or locale change. Keep the existing column
definitions and return behavior unchanged while preserving stable identity for
downstream useDataTable memoization.

Source: Coding guidelines

🤖 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 `@pkg/perf_metrics/admin.go`:
- Around line 153-194: Reduce hotBucketsMu lock scope across all affected sites:
in pkg/perf_metrics/admin.go lines 153-194, acquire the read lock immediately
before hotBuckets.Range and release it after traversal, leaving the three
database calls outside the lock; apply the same placement in
pkg/perf_metrics/metrics.go lines 90-92 and 140-142 for Query and
QuerySummaryAll; in pkg/perf_metrics/flush.go lines 23-26, drain buckets into a
local slice while holding the write lock, release it, then perform
UpsertPerfMetric calls.

In `@web/src/i18n/locales/zh-TW.json`:
- Line 152: Update the `Active groups` translation in the locale mapping from
`活躍用戶組` to the generic `活躍分組`, preserving the source meaning and surrounding
terminology.

---

Nitpick comments:
In `@controller/perf_metrics_admin_test.go`:
- Around line 22-27: Add a table-driven test case in the existing perf metrics
validation cases for a non-positive start_timestamp, using a query that sets
start_timestamp to zero or a negative value and a valid end_timestamp. Assert
the handler rejects it, covering the startTs <= 0 branch in the perf metrics
handler.

In `@model/perf_metric_admin_test.go`:
- Around line 10-23: Initialize the database explicitly before
TestGetPerfMetricGroupSummariesUsesHalfOpenRange accesses DB, using the
package’s established TestMain or an equivalent local fixture with AutoMigrate
support. Ensure the test remains self-contained and does not rely on bootstrap
behavior from another test file.

In `@pkg/perf_metrics/admin.go`:
- Around line 292-300: Refactor buildAdminModels and the related
countActiveAdminGroups/buildAdminGroups flow to group current and previous
counters by model name once before the per-model loop. Reuse each model’s
grouped counters for counting active groups and building groups, eliminating
repeated full-map scans while preserving existing results.

In `@web/src/features/dashboard/components/models/model-performance-columns.tsx`:
- Around line 123-127: Memoize the columns array returned by
useModelPerformanceColumns with useMemo, using [t, locale] as dependencies so it
is recreated only when translations or locale change. Keep the existing column
definitions and return behavior unchanged while preserving stable identity for
downstream useDataTable memoization.

In `@web/src/features/performance-metrics/lib/__tests__/admin.test.ts`:
- Around line 85-118: Extend the tests in the admin model performance helpers
suite to cover getAdminPerformanceDisplayState results for no_complete_buckets,
empty, and ready, preserving the documented precedence among all six states. Add
buildAdminPerformanceRows(undefined) coverage for its early return and a
multi-model fixture asserting each model is mapped correctly, while retaining
the existing error and disabled cases.
🪄 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 Plus

Run ID: ad6342b6-15d2-426a-9c99-592ee07dfe0c

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab0202 and 1fffec8.

📒 Files selected for processing (25)
  • controller/perf_metrics.go
  • controller/perf_metrics_admin_test.go
  • model/ability.go
  • model/perf_metric.go
  • model/perf_metric_admin_test.go
  • pkg/perf_metrics/admin.go
  • pkg/perf_metrics/admin_test.go
  • pkg/perf_metrics/flush.go
  • pkg/perf_metrics/metrics.go
  • router/api-router.go
  • web/src/components/data-table/hooks/use-data-table.ts
  • web/src/features/dashboard/components/models/model-performance-columns.tsx
  • web/src/features/dashboard/components/models/model-performance-table.tsx
  • web/src/features/dashboard/index.tsx
  • web/src/features/performance-metrics/api.ts
  • web/src/features/performance-metrics/lib/__tests__/admin.test.ts
  • web/src/features/performance-metrics/lib/admin.ts
  • web/src/features/performance-metrics/types.ts
  • web/src/i18n/locales/en.json
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/ru.json
  • web/src/i18n/locales/vi.json
  • web/src/i18n/locales/zh-TW.json
  • web/src/i18n/locales/zh.json

Comment thread pkg/perf_metrics/admin.go
Comment on lines +153 to +194
func readAdminCounters(currentPeriod AdminTimeRange, previousPeriod AdminTimeRange) (map[modelGroupKey]counters, map[modelGroupKey]counters, AdminAvailableRange, error) {
hotBucketsMu.RLock()
defer hotBucketsMu.RUnlock()

currentRows, err := model.GetPerfMetricGroupSummaries(currentPeriod.Start, currentPeriod.End)
if err != nil {
return nil, nil, AdminAvailableRange{}, err
}
previousRows, err := model.GetPerfMetricGroupSummaries(previousPeriod.Start, previousPeriod.End)
if err != nil {
return nil, nil, AdminAvailableRange{}, err
}
oldest, newest, err := model.GetPerfMetricAvailableRange()
if err != nil {
return nil, nil, AdminAvailableRange{}, err
}

current := adminCountersFromRows(currentRows)
previous := adminCountersFromRows(previousRows)
hotBuckets.Range(func(key, value any) bool {
bucket := key.(bucketKey)
snapshot := value.(*atomicBucket).snapshot()
if snapshot.requestCount == 0 {
return true
}
if oldest == nil || bucket.bucketTs < *oldest {
oldest = int64Pointer(bucket.bucketTs)
}
if newest == nil || bucket.bucketTs > *newest {
newest = int64Pointer(bucket.bucketTs)
}
if bucket.bucketTs >= currentPeriod.Start && bucket.bucketTs < currentPeriod.End {
mergeAdminCounters(current, modelGroupKey{model: bucket.model, group: bucket.group}, snapshot)
}
if bucket.bucketTs >= previousPeriod.Start && bucket.bucketTs < previousPeriod.End {
mergeAdminCounters(previous, modelGroupKey{model: bucket.model, group: bucket.group}, snapshot)
}
return true
})

return current, previous, AdminAvailableRange{OldestBucketTs: oldest, NewestBucketTs: newest}, nil
}

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

hotBucketsMu scope covers database I/O in four places. The new mutex only needs to protect traversal and draining of the in-memory hotBuckets map. Every current holder also spans synchronous database calls. Because a pending writer blocks new readers on a sync.RWMutex, one slow query or one slow flush stalls the flush loop and all metric queries.

  • pkg/perf_metrics/admin.go#L153-L194: move RLock/RUnlock in readAdminCounters below the three model.GetPerfMetric* calls, immediately around the hotBuckets.Range block.
  • pkg/perf_metrics/metrics.go#L90-L92: move the RLock/RUnlock in Query to just before the hotBuckets.Range call at line 114.
  • pkg/perf_metrics/metrics.go#L140-L142: move the RLock/RUnlock in QuerySummaryAll to just before the hotBuckets.Range call at line 162.
  • pkg/perf_metrics/flush.go#L23-L26: drain all buckets into a local slice under the write lock, release the lock, then run the UpsertPerfMetric calls.
📍 Affects 3 files
  • pkg/perf_metrics/admin.go#L153-L194 (this comment)
  • pkg/perf_metrics/metrics.go#L90-L92
  • pkg/perf_metrics/metrics.go#L140-L142
  • pkg/perf_metrics/flush.go#L23-L26
🤖 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 `@pkg/perf_metrics/admin.go` around lines 153 - 194, Reduce hotBucketsMu lock
scope across all affected sites: in pkg/perf_metrics/admin.go lines 153-194,
acquire the read lock immediately before hotBuckets.Range and release it after
traversal, leaving the three database calls outside the lock; apply the same
placement in pkg/perf_metrics/metrics.go lines 90-92 and 140-142 for Query and
QuerySummaryAll; in pkg/perf_metrics/flush.go lines 23-26, drain buckets into a
local slice while holding the write lock, release it, then perform
UpsertPerfMetric calls.

"Active apps": "活躍套用程式",
"Active Cache Count": "活躍緩存數",
"Active Files": "活躍檔案",
"Active groups": "活躍用戶組",

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep Active groups generic.

活躍用戶組 adds user, but the source refers to generic groups. The surrounding performance translations use 分組 for this concept. Use 活躍分組 to preserve the source meaning.

🤖 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/src/i18n/locales/zh-TW.json` at line 152, Update the `Active groups`
translation in the locale mapping from `活躍用戶組` to the generic `活躍分組`, preserving
the source meaning and surrounding terminology.

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.

1 participant