Skip to content

feat: 在使用日志统计 API 和 Dashboard 中添加 Token 统计数据(提示词/补全) - #4113

Closed
wanxiaoT wants to merge 1 commit into
QuantumNous:mainfrom
wanxiaoT:feature/token-stats
Closed

feat: 在使用日志统计 API 和 Dashboard 中添加 Token 统计数据(提示词/补全)#4113
wanxiaoT wants to merge 1 commit into
QuantumNous:mainfrom
wanxiaoT:feature/token-stats

Conversation

@wanxiaoT

@wanxiaoT wanxiaoT commented Apr 6, 2026

Copy link
Copy Markdown

功能描述

在使用日志统计接口和数据看板前端页面中新增 Token 用量的详细统计,方便管理员和用户查看某个用户/模型的具体 Token 消耗情况。

修改内容

后端

  • model/log.goStat 结构体新增 prompt_tokenscompletion_tokenstoken 字段,SQL 聚合使用 COALESCE 防止 NULL
  • model/usedata.go:新增 GetQuotaDataByUsernameAndModelGetQuotaDataByUserIdAndModel 查询函数,支持按模型名称过滤
  • controller/log.go:API 响应新增 token 统计字段
  • controller/usedata.go:新增 user_idmodel_name 查询参数支持

前端

  • Dashboard 搜索弹窗支持按用户 ID 和模型名称过滤
  • 使用日志列表展示 prompt tokens、completion tokens、总 token 数
  • 新增 RPM(每分钟请求数)和 TPM(每分钟 Token 数)统计

其他

  • 新增 5 种语言的 i18n 翻译(en/fr/ja/ru/vi)

使用方法

  1. 进入控制台 → 点击搜索按钮
  2. 填写用户名称和模型名称 → 点击查询
  3. 左上角显示使用的 token 量(prompt/completion/总数)

Summary by CodeRabbit

  • New Features

    • Model-based filtering added to quota/usage queries and UI.
    • Admins can search by user ID or username (with debounced ID→username lookup).
    • Usage stats now include prompt tokens, completion tokens, total tokens, RPM and TPM; RPM shown in logs UI.
  • Improvements

    • Query parameter handling and client-side input normalization for more reliable searches.
  • Localization

    • Added translations for new UI entries in EN/FR/JA/RU/VI.

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds token metrics (prompt_tokens, completion_tokens, token) to log/stat aggregations, introduces optional model_name filtering in quota queries, enables admin user_id search with debounce-driven username resolution, and updates frontend UI, hooks, API controllers, model queries, and i18n translations accordingly.

Changes

Cohort / File(s) Summary
Log stats & model layer
model/log.go, model/usedata.go
Extended Stat with prompt_tokens, completion_tokens, and token; switched SQL aggregations to COALESCE(SUM(...), 0); added model-aware query variants and updated GetAllQuotaDates signature/flow to accept userId and modelName.
API controllers
controller/log.go, controller/usedata.go
Expanded JSON responses to include token fields; GetAllQuotaDates/GetUserQuotaDates now read and forward model_name and user_id (with fallback) to model functions.
Dashboard UI & hooks
web/src/components/dashboard/modals/SearchModal.jsx, web/src/hooks/dashboard/useDashboardData.js
Added admin toggle for user_search_type, user_id numeric input, and model_name input; input normalization; debounced admin-only user_id→username resolution with AbortController and sequence counter; query assembly now uses URLSearchParams and includes model_name.
Usage logs UI & hook
web/src/components/table/usage-logs/UsageLogsActions.jsx, web/src/hooks/usage-logs/useUsageLogsData.jsx
Extended stat state to include prompt_tokens, completion_tokens, rpm, and tpm; added RPM tag in actions and ensured token metrics availability for display.
Internationalization
web/src/i18n/locales/en.json, .../fr.json, .../ja.json, .../ru.json, .../vi.json
Added translations for "找不到该值" → "Value not found" and "用户ID" → "User ID" (localized), and fixed trailing-comma/object formatting to append keys.

Sequence Diagram

sequenceDiagram
    participant Admin as Admin User
    participant UI as SearchModal / Hook
    participant API as Backend API
    participant DB as Database

    Admin->>UI: choose search mode (user_id) and enter id
    UI->>UI: debounce 400ms
    UI->>API: GET /api/users/resolve?user_id=... (abortable)
    API->>DB: query user by id
    DB-->>API: return username
    API-->>UI: username
    UI->>UI: set inputs.username
    UI->>API: GET /api/data?start=...&end=...&model_name=...&user_id/username=...
    API->>DB: query quota_data (optional model_name filter), aggregate tokens (prompt/completion) and rpm/tpm
    DB-->>API: return aggregated stats
    API-->>UI: JSON { quota, rpm, tpm, prompt_tokens, completion_tokens, token }
    UI->>UI: render stats and tags (quota, rpm, token, ...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • Calcium-Ion
  • creamlike1024
  • seefs001

Poem

🐰 I nibble tokens, prompt and end,
I hop from id to name to send,
Models filtered, stats in sight,
Admins search by day or night,
Quotas counted—hop!—all tidy and bright.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding token statistics (prompt/completion tokens) to the usage logs API and Dashboard.

✏️ 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.

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

🧹 Nitpick comments (3)
controller/log.go (1)

145-151: Changes look correct; consider minor cleanup.

The token statistics fields are properly exposed. Two optional improvements:

  1. Rename quotaNum to stat for consistency with GetLogsStat (line 105)
  2. Remove the stale commented code at line 151
♻️ Optional cleanup
-	quotaNum, err := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channel, group)
+	stat, err := model.SumUsedQuota(logType, startTimestamp, endTimestamp, modelName, username, tokenName, channel, group)
 	if err != nil {
 		common.ApiError(c, err)
 		return
 	}
-	//tokenNum := model.SumUsedToken(logType, startTimestamp, endTimestamp, modelName, username, tokenName)
 	c.JSON(200, gin.H{
 		"success": true,
 		"message": "",
 		"data": gin.H{
-			"quota":             quotaNum.Quota,
-			"token":             quotaNum.Token,
-			"prompt_tokens":     quotaNum.PromptTokens,
-			"completion_tokens": quotaNum.CompletionTokens,
-			"rpm":               quotaNum.Rpm,
-			"tpm":               quotaNum.Tpm,
-			//"token": tokenNum,
+			"quota":             stat.Quota,
+			"token":             stat.Token,
+			"prompt_tokens":     stat.PromptTokens,
+			"completion_tokens": stat.CompletionTokens,
+			"rpm":               stat.Rpm,
+			"tpm":               stat.Tpm,
 		},
 	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controller/log.go` around lines 145 - 151, Rename the local variable quotaNum
to stat to match the naming used in GetLogsStat and improve consistency, update
all references in the same block (e.g., "quota": quotaNum.Quota → stat.Quota),
and remove the stale commented line //"token": tokenNum so the block contains
only active, relevant fields.
web/src/hooks/dashboard/useDashboardData.js (2)

237-244: Minor: Redundant fallback in string conversion.

At line 238, message || '' is unnecessary since the else if (message) condition at line 237 guarantees message is truthy.

🔧 Simplify string conversion
         } else if (message) {
-          const msg = String(message || '');
+          const msg = String(message);
           if (msg.toLowerCase().includes('record not found')) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/dashboard/useDashboardData.js` around lines 237 - 244, The code
redundantly uses `message || ''` inside the `else if (message)` branch; replace
the conversion at the `const msg = String(message || '');` line with
`String(message)` (inside the same else-if block in useDashboardData.js) so the
truthy check is relied upon and the fallback is removed, leaving the subsequent
`msg.toLowerCase().includes('record not found')` and `showError(msg)` logic
unchanged.

324-324: Consider: Dependency array could be more precise.

Using the entire inputs object as a dependency means loadQuotaData is recreated whenever any input field changes, including unrelated ones like token_name or channel. This is functionally correct but could cause unnecessary re-renders in dependent effects/callbacks.

🔧 Optional: Use specific input fields in dependency array
-  }, [inputs, dataExportDefaultTime, isAdminUser]);
+  }, [
+    inputs.start_timestamp,
+    inputs.end_timestamp,
+    inputs.username,
+    inputs.user_id,
+    inputs.user_search_type,
+    inputs.model_name,
+    dataExportDefaultTime,
+    isAdminUser,
+  ]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/dashboard/useDashboardData.js` at line 324, The dependency
array is too broad because it uses the whole inputs object; update the hook that
defines loadQuotaData (the useCallback/useEffect that currently depends on
inputs, dataExportDefaultTime, isAdminUser) to depend only on the specific
properties of inputs that loadQuotaData actually reads (e.g.,
inputs.organizationId, inputs.workspaceId, inputs.teamId — replace with the real
property names used inside loadQuotaData) along with dataExportDefaultTime and
isAdminUser, so the callback is not recreated for unrelated changes like
token_name or channel.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/i18n/locales/en.json`:
- Around line 3380-3382: The two new translation keys "找不到该值" and "用户ID" were
added at the JSON root instead of inside the existing "translation" object; move
these keys into the "translation" object (before its closing brace) so i18next
can find them, ensuring they follow the same key/value format used by other
entries in that object.

In `@web/src/i18n/locales/ja.json`:
- Around line 3317-3320: The new translation keys "找不到该值" and "用户ID" were
accidentally placed outside the "translation" object (the closing brace on the
"translation" object at the line above makes them siblings), so move these
entries inside the existing "translation" object, ensuring proper JSON commas
and indentation so they become children of "translation" and follow the flat
JSON structure with Chinese source strings as keys used by i18next.

In `@web/src/i18n/locales/ru.json`:
- Around line 3350-3352: The two keys "找不到该值" and "用户ID" are defined outside the
top-level "translation" object so t('...') lookups will fail; move these entries
into the existing "translation" object in the ru.json locale (i.e., place
"找不到该值": "Значение не найдено" and "用户ID": "ID пользователя" as properties
inside the "translation" object), keeping the Chinese strings as keys and
aligning indentation/commas with the surrounding JSON structure.

In `@web/src/i18n/locales/vi.json`:
- Around line 3886-3888: The two new keys "找不到该值" and "用户ID" were added at the
JSON root instead of inside the "translation" object, which breaks lookups via
useTranslation() / t('中文key'); move these keys and their Vietnamese values into
the existing "translation" object in vi.json (preserve the Chinese strings as
keys and Vietnamese strings as values) so the file remains a flat translation
map usable by t(...).

---

Nitpick comments:
In `@controller/log.go`:
- Around line 145-151: Rename the local variable quotaNum to stat to match the
naming used in GetLogsStat and improve consistency, update all references in the
same block (e.g., "quota": quotaNum.Quota → stat.Quota), and remove the stale
commented line //"token": tokenNum so the block contains only active, relevant
fields.

In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 237-244: The code redundantly uses `message || ''` inside the
`else if (message)` branch; replace the conversion at the `const msg =
String(message || '');` line with `String(message)` (inside the same else-if
block in useDashboardData.js) so the truthy check is relied upon and the
fallback is removed, leaving the subsequent `msg.toLowerCase().includes('record
not found')` and `showError(msg)` logic unchanged.
- Line 324: The dependency array is too broad because it uses the whole inputs
object; update the hook that defines loadQuotaData (the useCallback/useEffect
that currently depends on inputs, dataExportDefaultTime, isAdminUser) to depend
only on the specific properties of inputs that loadQuotaData actually reads
(e.g., inputs.organizationId, inputs.workspaceId, inputs.teamId — replace with
the real property names used inside loadQuotaData) along with
dataExportDefaultTime and isAdminUser, so the callback is not recreated for
unrelated changes like token_name or channel.
🪄 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: 6a4f90ac-8a8e-42a7-a50f-0e8202ee8c50

📥 Commits

Reviewing files that changed from the base of the PR and between eacc245 and 32f7a5f.

📒 Files selected for processing (13)
  • controller/log.go
  • controller/usedata.go
  • model/log.go
  • model/usedata.go
  • web/src/components/dashboard/modals/SearchModal.jsx
  • web/src/components/table/usage-logs/UsageLogsActions.jsx
  • web/src/hooks/dashboard/useDashboardData.js
  • web/src/hooks/usage-logs/useUsageLogsData.jsx
  • 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
👮 Files not reviewed due to content moderation or server errors (7)
  • web/src/i18n/locales/fr.json
  • web/src/components/table/usage-logs/UsageLogsActions.jsx
  • web/src/hooks/usage-logs/useUsageLogsData.jsx
  • controller/usedata.go
  • web/src/components/dashboard/modals/SearchModal.jsx
  • model/log.go
  • model/usedata.go

Comment thread web/src/i18n/locales/en.json
Comment thread web/src/i18n/locales/ja.json
Comment on lines +3350 to +3352
},
"找不到该值": "Значение не найдено",
"用户ID": "ID пользователя"

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

Move new locale keys into the translation object

Line 3351 and Line 3352 are currently outside translation, so these keys likely won’t resolve via normal t('中文key') lookups.

✅ Suggested fix
-  },
-  "找不到该值": "Значение не найдено",
-  "用户ID": "ID пользователя"
+    "找不到该值": "Значение не найдено",
+    "用户ID": "ID пользователя"
+  }
 }

As per coding guidelines: web/src/i18n/**/*.json translation files must follow the project i18n structure with Chinese source strings as keys.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
},
"找不到该值": "Значение не найдено",
"用户ID": "ID пользователя"
"找不到该值": "Значение не найдено",
"用户ID": "ID пользователя"
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/i18n/locales/ru.json` around lines 3350 - 3352, The two keys "找不到该值"
and "用户ID" are defined outside the top-level "translation" object so t('...')
lookups will fail; move these entries into the existing "translation" object in
the ru.json locale (i.e., place "找不到该值": "Значение не найдено" and "用户ID": "ID
пользователя" as properties inside the "translation" object), keeping the
Chinese strings as keys and aligning indentation/commas with the surrounding
JSON structure.

Comment thread web/src/i18n/locales/vi.json Outdated
@Calcium-Ion

Copy link
Copy Markdown
Member

使用日志统计请勿修改当前UI设计,必须保持默认3个统计字段,请勿改动颜色设计

@wanxiaoT

wanxiaoT commented Apr 6, 2026

Copy link
Copy Markdown
Author

收到

…dashboard UI

- Add prompt_tokens, completion_tokens, total token fields to Stat struct

- Use COALESCE in DB aggregation to prevent NULL values

- Add model-scoped quota queries (GetQuotaDataByUsernameAndModel, GetQuotaDataByUserIdAndModel)

- Support user_id and model_name query parameters in dashboard API

- Display token counts in usage logs table and dashboard search modal

- Add i18n translations for new UI strings
@wanxiaoT
wanxiaoT force-pushed the feature/token-stats branch from 32f7a5f to d6de049 Compare April 6, 2026 14:59

@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 (1)
web/src/components/dashboard/modals/SearchModal.jsx (1)

135-142: Use useTranslation() inside the modal instead of passing t down.

These new filter labels keep extending the prop-drilled i18n surface; pulling t locally will keep the modal API smaller and matches the frontend convention.

As per coding guidelines, web/src/**/*.{ts,tsx,js,jsx}: Frontend i18n: Use useTranslation() hook and call t('中文key') in components.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/modals/SearchModal.jsx` around lines 135 - 142,
SearchModal is prop-drilled a translation function `t`; instead import and call
useTranslation() inside the modal to reduce props and follow frontend i18n
conventions. In the SearchModal component remove the `t` prop usage, add import
{ useTranslation } from 'react-i18next', call const { t } = useTranslation() at
the top of the component, and replace uses like createFormField(... label:
t('模型名称') ...) with the locally obtained t; also update the component signature
to stop accepting `t` so callers no longer need to pass it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/dashboard/modals/SearchModal.jsx`:
- Around line 97-133: SearchModal currently expects a t prop for translations;
refactor it to import and call const { t } = useTranslation() within the
SearchModal component instead of using the passed-in t prop. Remove t from the
component's props (and any PropTypes/TS types) and update usages inside JSX
(e.g., labels/placeholders in Form.RadioGroup, createFormField calls for
'username' and 'user_id') to use the locally scoped t; keep existing handlers
like handleInputChange and form field symbols (userSearchType, username,
user_id, createFormField, Form.RadioGroup, Form.Input, Form.InputNumber)
unchanged. Ensure the component imports useTranslation from react-i18next.

In `@web/src/hooks/dashboard/useDashboardData.js`:
- Around line 284-286: Trim the model_name before adding it to the URL params in
the useDashboardData hook: replace the direct use of model_name with a trimmed
value (e.g., const trimmedModel = model_name.trim()) and then call
params.set('model_name', trimmedModel) (and only set it when trimmedModel is
non-empty) so the backend exact-match filter behaves like username
normalization.

---

Nitpick comments:
In `@web/src/components/dashboard/modals/SearchModal.jsx`:
- Around line 135-142: SearchModal is prop-drilled a translation function `t`;
instead import and call useTranslation() inside the modal to reduce props and
follow frontend i18n conventions. In the SearchModal component remove the `t`
prop usage, add import { useTranslation } from 'react-i18next', call const { t }
= useTranslation() at the top of the component, and replace uses like
createFormField(... label: t('模型名称') ...) with the locally obtained t; also
update the component signature to stop accepting `t` so callers no longer need
to pass it.
🪄 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: af9dbb37-6800-4c0d-86a8-6b87ba59ac16

📥 Commits

Reviewing files that changed from the base of the PR and between 32f7a5f and d6de049.

📒 Files selected for processing (13)
  • controller/log.go
  • controller/usedata.go
  • model/log.go
  • model/usedata.go
  • web/src/components/dashboard/modals/SearchModal.jsx
  • web/src/components/table/usage-logs/UsageLogsActions.jsx
  • web/src/hooks/dashboard/useDashboardData.js
  • web/src/hooks/usage-logs/useUsageLogsData.jsx
  • 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
✅ Files skipped from review due to trivial changes (3)
  • web/src/i18n/locales/fr.json
  • web/src/i18n/locales/vi.json
  • web/src/i18n/locales/ru.json
🚧 Files skipped from review as they are similar to previous changes (8)
  • web/src/hooks/usage-logs/useUsageLogsData.jsx
  • controller/usedata.go
  • web/src/components/table/usage-logs/UsageLogsActions.jsx
  • controller/log.go
  • web/src/i18n/locales/ja.json
  • web/src/i18n/locales/en.json
  • model/usedata.go
  • model/log.go

Comment on lines +97 to +133
{isAdminUser && (
<Form.RadioGroup
field='user_search_type'
label={t('用户')}
initValue={userSearchType}
value={userSearchType}
onChange={(value) => handleInputChange(value, 'user_search_type')}
>
<Radio value='username'>{t('用户名')}</Radio>
<Radio value='user_id'>{t('用户ID')}</Radio>
</Form.RadioGroup>
)}

{isAdminUser &&
userSearchType === 'username' &&
createFormField(Form.Input, {
field: 'username',
label: t('用户名称'),
noLabel: true,
value: username,
placeholder: t('可选值'),
name: 'username',
onChange: (value) => handleInputChange(value, 'username'),
})}

{isAdminUser &&
userSearchType === 'user_id' &&
createFormField(Form.InputNumber, {
field: 'user_id',
noLabel: true,
value: user_id,
placeholder: t('可选值'),
name: 'user_id',
min: 1,
step: 1,
precision: 0,
onChange: (value) => handleInputChange(value, 'user_id'),
})}

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

🧩 Analysis chain

🏁 Script executed:

# First, find the SearchModal.jsx file and examine its contents
find . -name "SearchModal.jsx" -o -name "SearchModal.tsx" | head -20

Repository: QuantumNous/new-api

Length of output: 116


🏁 Script executed:

# Get file stats first
wc -l web/src/components/dashboard/modals/SearchModal.jsx 2>/dev/null || echo "Checking for alternative paths..."
fd -i searchmodal

Repository: QuantumNous/new-api

Length of output: 170


🏁 Script executed:

# List files in the dashboard/modals directory
git ls-files "web/src/components/dashboard/modals/" | head -20

Repository: QuantumNous/new-api

Length of output: 114


🏁 Script executed:

# Read the entire SearchModal.jsx file to understand context
cat -n web/src/components/dashboard/modals/SearchModal.jsx

Repository: QuantumNous/new-api

Length of output: 5397


🏁 Script executed:

# Search for where SearchModal is used and how handleInputChange is passed
rg -n "SearchModal" web/src --type jsx --type tsx -B 2 -A 5 | head -50

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Search for handleInputChange definition in the parent component
rg -n "handleInputChange" web/src/components/dashboard --type jsx --type tsx | head -30

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Search for SearchModal usage without type filter
rg -n "SearchModal" web/src -B 2 -A 5 | head -60

Repository: QuantumNous/new-api

Length of output: 4259


🏁 Script executed:

# Find the parent component that uses SearchModal
fd -e jsx -e tsx web/src/components/dashboard | xargs grep -l "SearchModal" 2>/dev/null

Repository: QuantumNous/new-api

Length of output: 482


🏁 Script executed:

# Find parent component files
fd . web/src/components/dashboard -name "*.jsx" -o -name "*.tsx" | xargs grep -l "SearchModal" 2>/dev/null

Repository: QuantumNous/new-api

Length of output: 295


🏁 Script executed:

# Examine the useDashboardData hook to understand state management and async logic
cat -n web/src/hooks/dashboard/useDashboardData.js | head -220

Repository: QuantumNous/new-api

Length of output: 8783


🏁 Script executed:

# Continue reading from line 220 onwards
tail -n +220 web/src/hooks/dashboard/useDashboardData.js | head -100

Repository: QuantumNous/new-api

Length of output: 3088


🏁 Script executed:

# Check if other components use useTranslation() or receive t as prop
rg -n "useTranslation" web/src/components/dashboard --type js --type jsx --type ts --type tsx | head -20

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Look at other dashboard modal components for comparison
fd . web/src/components/dashboard/modals -type f | head -10

Repository: QuantumNous/new-api

Length of output: 233


🏁 Script executed:

# Check the i18n usage pattern in the codebase
rg "const.*useTranslation" web/src/components --type js --type jsx --type ts --type tsx | head -30

Repository: QuantumNous/new-api

Length of output: 90


🏁 Script executed:

# Check useTranslation usage without type filter
rg "useTranslation" web/src/components/dashboard -B 1 -A 1 | head -40

Repository: QuantumNous/new-api

Length of output: 505


🏁 Script executed:

# List modal files
find web/src/components/dashboard/modals -type f

Repository: QuantumNous/new-api

Length of output: 114


🏁 Script executed:

# Check another dashboard component to see i18n pattern
cat -n web/src/components/dashboard/index.jsx | head -100

Repository: QuantumNous/new-api

Length of output: 4008


🏁 Script executed:

# Check how t is passed around - look for patterns in other modals/components
rg "const.*{.*t.*}.*useTranslation" web/src/components -C 2 | head -50

Repository: QuantumNous/new-api

Length of output: 183


🏁 Script executed:

# Check if components receive t as prop or use useTranslation
rg "SearchModal|useTranslation" web/src/components/dashboard -C 3 | grep -A 5 "SearchModal ="

Repository: QuantumNous/new-api

Length of output: 418


🏁 Script executed:

# Look at how other components are structured - check if they use local useTranslation
find web/src/components -name "*.jsx" -exec grep -l "useTranslation\|const.*=.*({.*t.*})" {} \; | head -20

Repository: QuantumNous/new-api

Length of output: 4106


🏁 Script executed:

# Look at the dashboard index to understand handleSearchConfirm
rg -n "handleSearchConfirm" web/src/components/dashboard -B 3 -A 10 | head -60

Repository: QuantumNous/new-api

Length of output: 4760


🏁 Script executed:

# Find where SearchModal is used and where handleSearchConfirm comes from
rg -n "handleSearchConfirm\|SearchModal" web/src/hooks/dashboard/useDashboardData.js | head -20

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Examine lines around the search confirm handler in the dashboard hook
sed -n '340,380p' web/src/hooks/dashboard/useDashboardData.js

Repository: QuantumNous/new-api

Length of output: 1102


🏁 Script executed:

# Check how handleInputChange is being called and what happens with the async username update
sed -n '144,189p' web/src/hooks/dashboard/useDashboardData.js

Repository: QuantumNous/new-api

Length of output: 1454


🏁 Script executed:

# Verify the exact behavior - does the form field actually get out of sync?
# Check if there are any other Form fields with the same pattern in the file
rg -n "value=\|initValue=" web/src/components/dashboard/modals/SearchModal.jsx

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Let me check the exact line numbers and structure of the form fields
sed -n '97,143p' web/src/components/dashboard/modals/SearchModal.jsx

Repository: QuantumNous/new-api

Length of output: 1541


🏁 Script executed:

# Check how other components in the codebase handle i18n - are they all receiving t as prop?
rg -n "const.*Modal.*=.*{" web/src/components --max-count 5 | head -20

Repository: QuantumNous/new-api

Length of output: 162


🏁 Script executed:

# Look at StatsCards to see how it uses useTranslation
cat -n web/src/components/dashboard/StatsCards.jsx | head -50

Repository: QuantumNous/new-api

Length of output: 2170


🏁 Script executed:

# Check Semi Form documentation/examples in the codebase to see how form fields are typically used
rg -n "formApi\|Form.Input\|Form.DatePicker" web/src/components --max-count 10 | head -50

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Verify: are there any uses of formApi.setValue in the codebase?
rg -n "formApi.setValue\|formApi.setValues" web/src

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Check if SearchModal is the only component receiving t as prop vs using hook
rg -n "const.*Modal.*=" web/src/components -A 5 | grep -E "const.*Modal|useTranslation|^[0-9]+-.*t," | head -40

Repository: QuantumNous/new-api

Length of output: 4500


🏁 Script executed:

# Look at how Form components are used elsewhere - check if value/onChange pattern is consistent
rg -n "Form.Input\|Form.DatePicker\|Form.Select" web/src/components -A 3 | grep -E "value=|onChange=" | head -30

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Check if there's any usage of formApi in the entire codebase
rg -n "formApi\|formRef.current" web/src --max-count 10

Repository: QuantumNous/new-api

Length of output: 45


🏁 Script executed:

# Verify the actual behavior: when username updates from async, does the form field receive it?
# Check the complete async user_id lookup effect
sed -n '199,263p' web/src/hooks/dashboard/useDashboardData.js

Repository: QuantumNous/new-api

Length of output: 1951


Use useTranslation() hook directly in this component instead of receiving t as a prop.

SearchModal should follow the i18n guideline by importing and calling const { t } = useTranslation() locally, consistent with other components like StatsCards and EditVendorModal. This eliminates prop drilling and aligns with the frontend i18n pattern specified in the coding guidelines.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/modals/SearchModal.jsx` around lines 97 - 133,
SearchModal currently expects a t prop for translations; refactor it to import
and call const { t } = useTranslation() within the SearchModal component instead
of using the passed-in t prop. Remove t from the component's props (and any
PropTypes/TS types) and update usages inside JSX (e.g., labels/placeholders in
Form.RadioGroup, createFormField calls for 'username' and 'user_id') to use the
locally scoped t; keep existing handlers like handleInputChange and form field
symbols (userSearchType, username, user_id, createFormField, Form.RadioGroup,
Form.Input, Form.InputNumber) unchanged. Ensure the component imports
useTranslation from react-i18next.

Comment on lines +284 to +286
if (model_name) {
params.set('model_name', model_name);
}

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

Trim model_name before appending it to the query.

The backend model filter is an exact model_name = ? match, so pasted leading/trailing whitespace here turns a valid search into an empty dataset. Normalizing it the same way as username avoids surprising misses.

✂️ Suggested fix
-      if (model_name) {
-        params.set('model_name', model_name);
+      const normalizedModelName = model_name?.trim();
+      if (normalizedModelName) {
+        params.set('model_name', normalizedModelName);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/dashboard/useDashboardData.js` around lines 284 - 286, Trim the
model_name before adding it to the URL params in the useDashboardData hook:
replace the direct use of model_name with a trimmed value (e.g., const
trimmedModel = model_name.trim()) and then call params.set('model_name',
trimmedModel) (and only set it when trimmedModel is non-empty) so the backend
exact-match filter behaves like username normalization.

@wanxiaoT wanxiaoT closed this by deleting the head repository Jul 15, 2026
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.

2 participants