Skip to content

fix(usage-logs): filter blank model options#1287

Open
ding113 wants to merge 3 commits into
devfrom
fix/issue-1285-usage-log-empty-model
Open

fix(usage-logs): filter blank model options#1287
ding113 wants to merge 3 commits into
devfrom
fix/issue-1285-usage-log-empty-model

Conversation

@ding113

@ding113 ding113 commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Filter empty and whitespace-only model names from usage log model filter options at the repository/API source.
  • Defensively filter model options in the lazy UI hook and model Select rendering so old or malformed responses cannot create empty Select.Item values.
  • Align the Biome schema reference with the locked Biome CLI version so lint can run locally.

Fixes #1285

Tests

  • bunx vitest run tests/unit/repository/usage-logs-model-filter-options.test.ts
  • bunx vitest run tests/unit/dashboard-logs-sessionid-suggestions-ui.test.tsx
  • bunx vitest run tests/api/v1/usage-logs/usage-logs.test.ts tests/api/v1/me/me.test.ts tests/unit/api/v1/usage-logs-hooks.test.ts
  • bun run build
  • bun run lint
  • bun run lint:fix
  • bun run typecheck
  • bun run test

Greptile Summary

This PR adds layered, defensive filtering of blank/whitespace-only model names that could otherwise create empty <SelectItem> values in the usage-log model filter dropdown. Filtering is applied at three independent layers: the SQL query (btrim), the API-response hook, and the UI component's useMemo.

  • Repository layer (usage-logs.ts): adds a btrim(model) <> '' SQL predicate and a JS-level isNonBlankString fallback so the DB query never returns blank strings.
  • Hook layer (use-lazy-filter-options.ts): wraps getModelList() with an isNonBlankString guard so malformed API responses are stripped before reaching state.
  • UI layer (request-filters.tsx): computes modelOptions via useMemo to prevent any remaining blanks from being rendered as <SelectItem> nodes, and biome.json is bumped to match the locked CLI version.

Confidence Score: 5/5

Safe to merge — the change only adds filtering guards at multiple layers, with no mutations to data writes or auth paths.

The change is purely additive: blank-string filtering is applied at the SQL predicate, the API hook, and the UI rendering stage. No existing data is modified, no auth or write paths are touched, and each new layer has a corresponding test.

No files require special attention.

Important Files Changed

Filename Overview
src/repository/usage-logs.ts Adds btrim(model) <> '' SQL predicate and JS-level isNonBlankString fallback to strip blank model entries from filter options.
src/app/[locale]/dashboard/logs/_hooks/use-lazy-filter-options.ts Wraps getModelList() with an isNonBlankString post-filter as a defensive layer against malformed API responses; adds a file-level isNonBlankString helper.
src/app/[locale]/dashboard/logs/_components/filters/request-filters.tsx Adds a modelOptions memo that strips blank strings from the models array before rendering SelectItem nodes.
tests/unit/repository/usage-logs-model-filter-options.test.ts New unit test verifying the JS-level isNonBlankString filter in getUsedModels; SQL WHERE conditions are not asserted (noted in prior review).
tests/unit/dashboard-logs-lazy-filter-options.test.tsx New hook-level test confirming that blank, null, and non-string values returned from getModelList are dropped by useLazyModels.
tests/unit/dashboard-logs-sessionid-suggestions-ui.test.tsx Extends existing UI test with a case asserting blank model strings are excluded from rendered SelectItem values; upgrades SelectItem mock to expose data-select-value.
biome.json Schema reference bumped from 2.4.15 to 2.4.16 to match the locked Biome CLI version.

Reviews (2): Last reviewed commit: "fix(logs): drop non-string entries from ..." | Re-trigger Greptile

ding113 added 2 commits June 22, 2026 20:48
Blank or whitespace-only model values appeared in the model filter
dropdown. Add SQL-level btrim exclusion in getUsedModels, filter
blank entries in the lazy-loading hook, and guard the UI select
rendering. Includes unit tests for repository and UI layers.

Fixes #1285
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

修复了模型下拉框因数据库中存在空/空白字符串模型名而触发 Select.Item 不允许空 value 报错的问题。过滤逻辑在三层同时实施:数据库查询(getUsedModels)、懒加载 Hook(useLazyModels)以及 UI 组件(RequestFilters)。同时新增对应的单元测试并将 Biome schema 版本升至 2.4.16。

Changes

空白模型字符串过滤

Layer / File(s) Summary
Repository 层过滤与类型守卫
src/repository/usage-logs.ts, tests/unit/repository/usage-logs-model-filter-options.test.ts
getUsedModels() 在 DB 查询中新增 trim 非空约束,返回结果通过本地 isNonBlankString 类型守卫二次过滤;新增单元测试验证混合空/空白/有效/null 数据时只返回有效模型名。
useLazyModels Hook 层过滤
src/app/[locale]/dashboard/logs/_hooks/use-lazy-filter-options.ts
useLazyModels 的 fetcher 改为自定义异步函数,成功后对 result.data 应用 isNonBlankString 过滤;新增同名辅助函数。
RequestFilters UI 层过滤与测试
src/app/[locale]/dashboard/logs/_components/filters/request-filters.tsx, tests/unit/dashboard-logs-sessionid-suggestions-ui.test.tsx
新增 modelOptions useMemo 从 models 筛除空白项后渲染;更新 SelectItem mock 以输出 data-select-value 属性,新增测试用例断言空/空白模型不出现在下拉选项中。

Biome 工具链版本更新

Layer / File(s) Summary
Biome schema 版本升级
biome.json
$schema URL 版本号由 2.4.15 更新为 2.4.16。

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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
Title check ✅ Passed 标题 'fix(usage-logs): filter blank model options' 准确总结了PR的主要变更——过滤掉空白模型选项。
Linked Issues check ✅ Passed 代码变更充分满足#1285的所有目标:在仓储层、UI钩子层和组件层实现了空白/仅空白字符串的检测和过滤,防止空字符串值导致Select组件错误。
Out of Scope Changes check ✅ Passed 所有变更都直接关联到修复#1285的空白模型选项问题,包括biome.json更新是为了本地开发支持,未发现超出范围的修改。
Description check ✅ Passed PR 描述准确关联了代码变更,清晰阐述了分层过滤空白模型名称的解决方案、相关的问题修复以及详细的测试步骤。

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1285-usage-log-empty-model

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request filters out empty or blank model names from the model filter options. This filtering is implemented at the database query level, within the useLazyModels hook, and in the RequestFilters UI component, with accompanying tests added to verify the behavior. The reviewer feedback correctly identifies that filtering inside the UI component is redundant since it is already handled at the hook and database levels. The reviewer suggests removing this redundant filtering, mapping directly over models, and updating the test mocks to simulate the hook's filtering behavior.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

() => new Map(providers.map((provider) => [provider.id, provider.name])),
[providers]
);
const modelOptions = useMemo(() => models.filter((model) => model.trim().length > 0), [models]);

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.

medium

The models array returned by the useLazyModels() hook is already filtered to exclude empty and blank strings (both at the hook level and the repository/API level). Therefore, defining modelOptions with another filter inside useMemo is redundant. We can remove this useMemo entirely and use models directly.

<SelectContent>
<SelectItem value="all">{t("logs.filters.allModels")}</SelectItem>
{models.map((model) => (
{modelOptions.map((model) => (

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.

medium

Since modelOptions is redundant and can be removed, we should map over models directly here.

Suggested change
{modelOptions.map((model) => (
{models.map((model) => (

vi.mock("@/app/[locale]/dashboard/logs/_hooks/use-lazy-filter-options", () => ({
useLazyModels: () => ({
data: [],
data: lazyFilterOptionMocks.models,

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.

medium

Since the filtering responsibility has been moved to the useLazyModels hook, the mock for useLazyModels in this test should simulate this behavior by filtering the mocked models. This allows us to remove the redundant filtering from the RequestFilters component while keeping the integration test green and accurate.

Suggested change
data: lazyFilterOptionMocks.models,
data: lazyFilterOptionMocks.models.filter((m) => m.trim().length > 0),

@github-actions github-actions Bot added bug Something isn't working area:UI area:core labels Jun 22, 2026
Comment on lines +1473 to +1477
and(
isNull(messageRequest.deletedAt),
sql`${messageRequest.model} IS NOT NULL`,
sql`btrim(${messageRequest.model}) <> ''`
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 The IS NOT NULL condition is now redundant. In PostgreSQL, btrim(NULL) returns NULL, and NULL <> '' evaluates to NULL (not TRUE), so null rows are already excluded by the btrim condition. Keeping it is harmless, but removing it simplifies the predicate.

Suggested change
and(
isNull(messageRequest.deletedAt),
sql`${messageRequest.model} IS NOT NULL`,
sql`btrim(${messageRequest.model}) <> ''`
)
and(
isNull(messageRequest.deletedAt),
sql`btrim(${messageRequest.model}) <> ''`
)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/repository/usage-logs.ts
Line: 1473-1477

Comment:
The `IS NOT NULL` condition is now redundant. In PostgreSQL, `btrim(NULL)` returns `NULL`, and `NULL <> ''` evaluates to `NULL` (not `TRUE`), so null rows are already excluded by the `btrim` condition. Keeping it is harmless, but removing it simplifies the predicate.

```suggestion
      and(
        isNull(messageRequest.deletedAt),
        sql`btrim(${messageRequest.model}) <> ''`
      )
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +14 to +25

const selectDistinctMock = vi.fn(() =>
createThenableQuery([
{ model: "" },
{ model: " " },
{ model: "claude-sonnet-4-5" },
{ model: "gpt-4o" },
{ model: null },
])
);

vi.doMock("@/drizzle/db", () => ({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 SQL WHERE conditions are not asserted in the test

The mock's where function ignores its arguments and always returns the same pre-populated result set (including blank/null rows). This means the test only exercises the JavaScript post-processing filter in getUsedModels, not the btrim / IS NOT NULL SQL conditions. If those SQL conditions were accidentally removed, this test would still pass because isNonBlankString on line 1481 catches the blanks at the JS level. Consider adding an assertion that where received the expected SQL conditions, or document that SQL coverage is intentionally deferred to integration tests.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/unit/repository/usage-logs-model-filter-options.test.ts
Line: 14-25

Comment:
**SQL WHERE conditions are not asserted in the test**

The mock's `where` function ignores its arguments and always returns the same pre-populated result set (including blank/null rows). This means the test only exercises the JavaScript post-processing filter in `getUsedModels`, not the `btrim` / `IS NOT NULL` SQL conditions. If those SQL conditions were accidentally removed, this test would still pass because `isNonBlankString` on line 1481 catches the blanks at the JS level. Consider adding an assertion that `where` received the expected SQL conditions, or document that SQL coverage is intentionally deferred to integration tests.

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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 `@src/app/`[locale]/dashboard/logs/_hooks/use-lazy-filter-options.ts:
- Around line 103-106: The code at lines 123-125 directly calls value.trim()
without proper runtime type checking, which will throw an exception if the
upstream data contains null, undefined, or non-string values. Add a proper type
guard that checks whether the value is actually a string before calling trim()
on it. This ensures that malformed or unexpected data from the upstream response
won't cause the model dropdown loading to fail, and makes the defensive
filtering truly robust against all data type violations.
🪄 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: 0c663e03-bcd0-4077-8124-f3e8590f0c3f

📥 Commits

Reviewing files that changed from the base of the PR and between f96d00f and 931690e.

📒 Files selected for processing (6)
  • biome.json
  • src/app/[locale]/dashboard/logs/_components/filters/request-filters.tsx
  • src/app/[locale]/dashboard/logs/_hooks/use-lazy-filter-options.ts
  • src/repository/usage-logs.ts
  • tests/unit/dashboard-logs-sessionid-suggestions-ui.test.tsx
  • tests/unit/repository/usage-logs-model-filter-options.test.ts

Comment thread src/app/[locale]/dashboard/logs/_hooks/use-lazy-filter-options.ts
@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@github-actions github-actions Bot added the size/S Small PR (< 200 lines) label Jun 22, 2026

@github-actions github-actions 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.

Code Review Summary

No significant issues identified in this PR.

PR Size: S

  • Lines changed: 127 (116 additions, 11 deletions)
  • Files changed: 6

Changes Analysis

This PR implements a well-designed three-layer defensive approach to filter blank model names from the usage-logs filter UI:

  1. SQL Layer (usage-logs.ts): PostgreSQL btrim() condition filters at query time
  2. Hook Layer (use-lazy-filter-options.ts): JS filter on API response
  3. Component Layer (request-filters.tsx): Memoized filter before render

The defense-in-depth strategy is sound. If any layer fails or has stale data, the subsequent layers prevent blank Select.Item values from reaching the UI.

Coverage Assessment

  • ✅ Unit test validates JS isNonBlankString filter behavior
  • ✅ UI test verifies Select.Item rendering excludes blank values
  • ✅ Biome schema aligned with CLI version

Note: The SQL btrim WHERE condition is not asserted in the unit test (the mock ignores where() arguments). This is an acceptable gap given:

  • Integration tests cover end-to-end behavior
  • The JS filter provides a reliable safety net
  • SQL filter is primarily a performance optimization

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 0 0 0
Simplification 0 0 0 0

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - Clean
  • Type safety - Clean
  • Documentation accuracy - Clean
  • Test coverage - Adequate
  • Code clarity - Good

Automated review by Claude AI

isNonBlankString assumed its argument was always a string, so
malformed API responses containing null, numbers, or other
non-string values could slip through the blank check and pollute
filter dropdowns or cause runtime errors.

Widen the parameter to unknown and convert the helper into a type
guard that rejects non-string values before trimming. Add a test
verifying useLazyModels drops malformed entries and retains only
valid non-blank strings.

Fixes #1285
@github-actions

Copy link
Copy Markdown
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core area:UI bug Something isn't working size/S Small PR (< 200 lines)

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant