feat(logs): make cost column toggleable with improved type safety#715
feat(logs): make cost column toggleable with improved type safety#715ding113 merged 6 commits intoding113:devfrom
Conversation
Co-authored-by: Wine Fox <fox@ling.plus>
Co-authored-by: Wine Fox <fox@ling.plus>
Co-authored-by: Wine Fox <fox@ling.plus>
…st coverage - Remove duplicate VirtualizedLogsTableColumn type, reuse LogsTableColumn from column-visibility.ts - Add toggleColumn cost column visibility test to ensure proper toggle functionality Co-authored-by: Wine Fox <fox@ling.plus>
📝 WalkthroughWalkthrough将“cost”作为可切换的日志列引入:更新列可见性类型与默认配置,调整虚拟化表格与使用日志表的渲染与工具提示,添加相关单元测试;同时更新 .gitignore 并新增 v0.5.3 的 CHANGELOG 条目(文档性修改)。 Changes
估计代码审查工作量🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
概述本次提交为日志仪表板引入了新的"成本"列,包括列可见性切换、类型定义更新、UI 组件逻辑和测试覆盖。更新了类型签名、日志表格渲染,并添加了相关的配置和文档。 变更内容
估计代码审查工作量🎯 3 (Moderate) | ⏱️ ~25 分钟 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello @Lynricsy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant enhancement to the logs table by allowing users to dynamically show or hide the 'cost' column, improving customization of the user interface. Concurrently, it refactors the codebase to eliminate redundant type definitions, leading to a more robust and maintainable type system. The changes are thoroughly validated with new test coverage, ensuring the reliability of the column visibility feature. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request successfully makes the 'cost' column in the logs table toggleable, which is a great enhancement for user customization. The refactoring to consolidate duplicated type definitions into a single source of truth significantly improves type safety and maintainability. Additionally, the new tests for the column visibility functionality ensure the changes are well-covered. I have one suggestion to further improve code maintainability by reducing some repetitive logic.
There was a problem hiding this comment.
Code Review Summary
This PR successfully implements cost column toggleability with improved type safety. The changes are clean, well-tested, and follow project conventions.
PR Size: XS
- Lines changed: 150 (95 additions + 55 deletions)
- Files changed: 6
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
Key Improvements in This PR
- Type Safety: Removed duplicate
VirtualizedLogsTableColumntype definition, now reuses sharedLogsTableColumntype to prevent type drift - Feature Completeness: Cost column moved from
ALWAYS_VISIBLE_COLUMNSto toggleable columns, following same pattern as other columns - Test Coverage: Added comprehensive tests for cost column toggle functionality
- i18n Compliance: Verified
logs.columns.costkey exists in all 5 supported locales
The refactoring to consolidate duplicate types is particularly valuable for long-term maintainability.
Automated review by Claude AI
There was a problem hiding this comment.
Code Review Summary
This PR successfully implements cost column toggleability with improved type safety. The changes are clean, well-tested, and follow project conventions.
PR Size: XS
- Lines changed: 150 (95 additions + 55 deletions)
- Files changed: 6
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
Key Improvements in This PR
- Type Safety: Removed duplicate VirtualizedLogsTableColumn type definition, now reuses shared LogsTableColumn type to prevent type drift
- Feature Completeness: Cost column moved from ALWAYS_VISIBLE_COLUMNS to toggleable columns, following same pattern as other columns
- Test Coverage: Added comprehensive tests for cost column toggle functionality
- i18n Compliance: Verified logs.columns.cost key exists in all 5 supported locales
The refactoring to consolidate duplicate types is particularly valuable for long-term maintainability.
Automated review by Claude AI
There was a problem hiding this comment.
Pull request overview
This PR makes the logs table cost column user-toggleable via the existing column-visibility system, improves type sharing across components, and updates tests and metadata accordingly.
Changes:
- Extend
LogsTableColumnandDEFAULT_VISIBLE_COLUMNSto include"cost"and adjustALWAYS_VISIBLE_COLUMNSsocostis no longer classified as always-visible. - Update the virtualized logs table and column-visibility dropdown to use the shared
LogsTableColumntype and to conditionally render the cost column based onhiddenColumns. - Add unit tests for cost visibility behavior in the column-visibility utilities, and update CHANGELOG and
.gitignore.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/lib/column-visibility.ts |
Adds "cost" to the LogsTableColumn union and DEFAULT_VISIBLE_COLUMNS, and updates ALWAYS_VISIBLE_COLUMNS so cost is no longer treated as always visible, enabling persistence of the cost column’s visibility. |
src/lib/column-visibility.test.ts |
Extends tests to assert that DEFAULT_VISIBLE_COLUMNS includes "cost" and that toggleColumn correctly hides/shows "cost" and updates visible columns. |
src/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsx |
Replaces the local VirtualizedLogsTableColumn type with the shared LogsTableColumn, derives a hideCostColumn flag from hiddenColumns, and conditionally renders both the header and body cost column segments. |
src/app/[locale]/dashboard/logs/_components/column-visibility-dropdown.tsx |
Adds a label key mapping for the cost column so it appears in the column-visibility dropdown and can be toggled. |
src/app/[locale]/dashboard/logs/_components/usage-logs-view-virtualized.tsx |
Wires the ColumnVisibilityDropdown’s hiddenColumns into VirtualizedLogsTable, allowing cost (and other columns) to be hidden in the full-screen virtualized logs view. |
CHANGELOG.md |
Adds a new v0.5.3 release section summarizing unrelated feature, optimization, and bugfix work for the upcoming release. |
.gitignore |
Ignores the .ace-tool/ directory to avoid committing local tooling artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| {/* Cost */} | ||
| <div className="flex-[0.7] min-w-[60px] text-right font-mono text-xs px-1.5"> | ||
| {isNonBilling ? ( | ||
| "-" | ||
| ) : log.costUsd ? ( | ||
| <TooltipProvider> | ||
| <Tooltip delayDuration={250}> | ||
| <TooltipTrigger asChild> | ||
| <span className="cursor-help inline-flex items-center gap-1"> | ||
| {formatCurrency(log.costUsd, currencyCode, 6)} | ||
| {hideCostColumn ? null : ( | ||
| <div className="flex-[0.7] min-w-[60px] text-right font-mono text-xs px-1.5"> | ||
| {isNonBilling ? ( | ||
| "-" | ||
| ) : log.costUsd ? ( | ||
| <TooltipProvider> | ||
| <Tooltip delayDuration={250}> | ||
| <TooltipTrigger asChild> | ||
| <span className="cursor-help inline-flex items-center gap-1"> | ||
| {formatCurrency(log.costUsd, currencyCode, 6)} | ||
| {log.context1mApplied && ( | ||
| <Badge | ||
| variant="outline" | ||
| className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800" | ||
| > | ||
| 1M | ||
| </Badge> | ||
| )} | ||
| </span> | ||
| </TooltipTrigger> | ||
| <TooltipContent | ||
| align="end" | ||
| className="text-xs space-y-1 max-w-[300px]" | ||
| > | ||
| {log.context1mApplied && ( | ||
| <Badge | ||
| variant="outline" | ||
| className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800" | ||
| > | ||
| 1M | ||
| </Badge> | ||
| <div className="text-purple-600 dark:text-purple-400 font-medium"> | ||
| {t("logs.billingDetails.context1m")} | ||
| </div> | ||
| )} | ||
| </span> | ||
| </TooltipTrigger> | ||
| <TooltipContent align="end" className="text-xs space-y-1 max-w-[300px]"> | ||
| {log.context1mApplied && ( | ||
| <div className="text-purple-600 dark:text-purple-400 font-medium"> | ||
| {t("logs.billingDetails.context1m")} | ||
| <div> | ||
| {t("logs.billingDetails.input")}:{" "} | ||
| {formatTokenAmount(log.inputTokens)} tokens | ||
| </div> | ||
| )} | ||
| <div> | ||
| {t("logs.billingDetails.input")}:{" "} | ||
| {formatTokenAmount(log.inputTokens)} tokens | ||
| </div> | ||
| <div> | ||
| {t("logs.billingDetails.output")}:{" "} | ||
| {formatTokenAmount(log.outputTokens)} tokens | ||
| </div> | ||
| </TooltipContent> | ||
| </Tooltip> | ||
| </TooltipProvider> | ||
| ) : ( | ||
| "-" | ||
| )} | ||
| </div> | ||
| <div> | ||
| {t("logs.billingDetails.output")}:{" "} | ||
| {formatTokenAmount(log.outputTokens)} tokens | ||
| </div> | ||
| </TooltipContent> | ||
| </Tooltip> | ||
| </TooltipProvider> | ||
| ) : ( | ||
| "-" | ||
| )} | ||
| </div> | ||
| )} | ||
|
|
There was a problem hiding this comment.
The new hideCostColumn conditional render path isn’t covered by tests, even though similar column-visibility behavior for provider is explicitly tested in virtualized-logs-table.test.tsx. To avoid regressions (e.g., the cost column accidentally becoming always visible again), consider adding tests that verify the cost header and body cells are hidden/shown appropriately when hiddenColumns includes/excludes "cost".
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx:
- Around line 612-615: The current rendering uses a truthy check on log.costUsd
which treats 0 as missing and renders "-"; update the conditional in the
virtualized-logs-table component so it checks explicitly for null/undefined
(e.g., use log.costUsd == null) instead of a truthy test before rendering the
TooltipProvider branch; touch the isNonBilling branch and the expression that
reads log.costUsd so zero values are rendered correctly.
- Around line 621-646: Replace the hardcoded "1M" badge label and the literal
"tokens" suffixes with i18n keys: update the Badge content to use
t("logs.billingDetails.context1m") or a new key like
t("logs.billingDetails.context1mLabel") and replace the "tokens" text in the
TooltipContent lines with t("logs.billingDetails.tokens") so the strings for the
badge and the token unit are localized for zh-CN/zh-TW/en/ja/ru; ensure you call
the same i18n function used in this file (t) and keep the numeric values
produced by formatTokenAmount(log.inputTokens) and
formatTokenAmount(log.outputTokens) unchanged.
| <Badge | ||
| variant="outline" | ||
| className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800" | ||
| > | ||
| 1M | ||
| </Badge> | ||
| )} | ||
| </span> | ||
| </TooltipTrigger> | ||
| <TooltipContent | ||
| align="end" | ||
| className="text-xs space-y-1 max-w-[300px]" | ||
| > | ||
| {log.context1mApplied && ( | ||
| <Badge | ||
| variant="outline" | ||
| className="text-[10px] leading-tight px-1 bg-purple-50 text-purple-700 border-purple-200 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-800" | ||
| > | ||
| 1M | ||
| </Badge> | ||
| <div className="text-purple-600 dark:text-purple-400 font-medium"> | ||
| {t("logs.billingDetails.context1m")} | ||
| </div> | ||
| )} | ||
| </span> | ||
| </TooltipTrigger> | ||
| <TooltipContent align="end" className="text-xs space-y-1 max-w-[300px]"> | ||
| {log.context1mApplied && ( | ||
| <div className="text-purple-600 dark:text-purple-400 font-medium"> | ||
| {t("logs.billingDetails.context1m")} | ||
| <div> | ||
| {t("logs.billingDetails.input")}:{" "} | ||
| {formatTokenAmount(log.inputTokens)} tokens | ||
| </div> | ||
| )} | ||
| <div> | ||
| {t("logs.billingDetails.input")}:{" "} | ||
| {formatTokenAmount(log.inputTokens)} tokens | ||
| </div> | ||
| <div> | ||
| {t("logs.billingDetails.output")}:{" "} | ||
| {formatTokenAmount(log.outputTokens)} tokens | ||
| </div> | ||
| </TooltipContent> | ||
| </Tooltip> | ||
| </TooltipProvider> | ||
| ) : ( | ||
| "-" | ||
| )} | ||
| </div> | ||
| <div> | ||
| {t("logs.billingDetails.output")}:{" "} | ||
| {formatTokenAmount(log.outputTokens)} tokens | ||
| </div> |
There was a problem hiding this comment.
将成本提示中的 "1M" 与 "tokens" 文案接入 i18n。
当前为硬编码字符串,需改为翻译键并补齐各语言文案。
建议修改
- 1M
+ {t("logs.billingDetails.context1mShort")}
...
- {formatTokenAmount(log.inputTokens)} tokens
+ {formatTokenAmount(log.inputTokens)} {t("logs.billingDetails.tokensUnit")}
...
- {formatTokenAmount(log.outputTokens)} tokens
+ {formatTokenAmount(log.outputTokens)} {t("logs.billingDetails.tokensUnit")}🤖 Prompt for AI Agents
In `@src/app/`[locale]/dashboard/logs/_components/virtualized-logs-table.tsx
around lines 621 - 646, Replace the hardcoded "1M" badge label and the literal
"tokens" suffixes with i18n keys: update the Badge content to use
t("logs.billingDetails.context1m") or a new key like
t("logs.billingDetails.context1mLabel") and replace the "tokens" text in the
TooltipContent lines with t("logs.billingDetails.tokens") so the strings for the
badge and the token unit are localized for zh-CN/zh-TW/en/ja/ru; ensure you call
the same i18n function used in this file (t) and keep the numeric values
produced by formatTokenAmount(log.inputTokens) and
formatTokenAmount(log.outputTokens) unchanged.
Change costUsd falsy check to null check so \$0.00 displays properly instead of showing "-" for legitimate zero-cost requests. Co-authored-by: Wine Fox <fox@ling.plus>
* fix(proxy): add 'cannot be modified' error detection to thinking signature rectifier Extend the thinking signature rectifier to detect and handle the Anthropic API error when thinking/redacted_thinking blocks have been modified from their original response. This error occurs when clients inadvertently modify these blocks in multi-turn conversations. The rectifier will now remove these blocks and retry the request, similar to how it handles other thinking-related signature errors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore(deps): bump jspdf in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [jspdf](https://github.com/parallax/jsPDF). Updates `jspdf` from 3.0.4 to 4.1.0 - [Release notes](https://github.com/parallax/jsPDF/releases) - [Changelog](https://github.com/parallax/jsPDF/blob/master/RELEASE.md) - [Commits](parallax/jsPDF@v3.0.4...v4.1.0) --- updated-dependencies: - dependency-name: jspdf dependency-version: 4.1.0 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> * fix: Hot-reload cache invalidation for Request Filters and Sensitive Words (#710) * fix: hot-reload request filters via globalThis singleton pattern EventEmitter and RequestFilterEngine now use globalThis caching to ensure the same instance is shared across different Next.js worker contexts. This fixes the issue where filter changes required Docker restart. Added diagnostic logging for event subscription and propagation. * fix(redis): wait for subscriber connection ready before subscribe - ensureSubscriber now returns Promise<Redis>, waits for 'ready' event - subscribeCacheInvalidation returns null on failure instead of noop - RequestFilterEngine checks cleanup !== null before logging success - Fixes false "Subscribed" log when Redis connection fails * feat(sensitive-words): add hot-reload via Redis pub/sub Enable real-time cache invalidation for sensitive words detector, matching the pattern used by request-filter-engine and error-rule-detector. * fix(redis): harden cache invalidation subscriptions Ensure sensitive-words CRUD emits update events so hot-reload propagates across workers. Roll back failed pub/sub subscriptions, add retry/timeout coverage, and avoid sticky provider-cache subscription state. * fix(codex): bump default User-Agent fallback Update the hardcoded Codex UA used when requests lack an effective user-agent (e.g. filtered out). Keep unit tests in sync with the new default. * fix(redis): resubscribe cache invalidation after reconnect Clear cached subscription state on disconnect and resubscribe on ready so cross-worker cache invalidation survives transient Redis reconnects. Add unit coverage, avoid misleading publish logs, track detector cleanup handlers, and translate leftover Russian comments to English. * fix(sensitive-words): use globalThis singleton detector Align SensitiveWordDetector with existing __CCH_* singleton pattern to avoid duplicate instances across module reloads. Extend singleton unit tests to cover the detector. * chore: format code (req-fix-dda97fd) * fix: address PR review comments - pubsub.ts: use `once` instead of `on` for ready event to prevent duplicate resubscription handlers on reconnect - forwarder.ts: extract DEFAULT_CODEX_USER_AGENT constant - provider-cache.ts: wrap subscribeCacheInvalidation in try/catch - tests: use exported constant instead of hardcoded UA string * fix(redis): resubscribe across repeated reconnects Ensure pub/sub resubscribe runs on every reconnect, extend unit coverage, and keep emitRequestFiltersUpdated resilient when logger import fails. --------- Co-authored-by: John Doe <johndoe@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * feat(logs): make cost column toggleable with improved type safety (#715) close #713 * fix(proxy): add OpenAI chat completion format support in usage extraction (#705) (#716) The `extractUsageMetrics` function was missing support for OpenAI chat completion format fields (`prompt_tokens`/`completion_tokens`), causing token statistics to not be recorded for OpenAI-compatible providers. Changes: - Add `prompt_tokens` -> `input_tokens` mapping - Add `completion_tokens` -> `output_tokens` mapping - Preserve priority: Claude > Gemini > OpenAI format - Add 5 unit tests for OpenAI format handling Closes #705 Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * fix(currency): respect system currencyDisplay setting in UI (#717) Fixes #678 - Currency display unit configuration was not applied. Root cause: - `users-page-client.tsx` hardcoded `currencyCode="USD"` - `UserLimitBadge` and `LimitStatusIndicator` had hardcoded `unit="$"` default - `big-screen/page.tsx` used hardcoded "$" in multiple places Changes: - Add `getCurrencySymbol()` helper function to currency.ts - Fetch system settings in `users-page-client.tsx` and pass to table - Pass `currencySymbol` from `user-key-table-row.tsx` to limit badges - Remove hardcoded "$" defaults from badge components - Update big-screen page to fetch settings and use dynamic symbol - Add unit tests for `getCurrencySymbol` Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * feat(gemini): add Google Search web access preference for Gemini providers (#721) * feat(gemini): add Google Search web access preference for Gemini providers Add provider-level preference for Gemini API type providers to control Google Search (web access) tool injection: - inherit: Follow client request (default) - enabled: Force inject googleSearch tool into request - disabled: Force remove googleSearch tool from request Changes: - Add geminiGoogleSearchPreference field to provider schema - Add GeminiGoogleSearchPreference type and validation - Implement applyGeminiGoogleSearchOverride with audit trail - Add UI controls in provider form (Gemini Overrides section) - Add i18n translations for 5 languages (en, zh-CN, zh-TW, ja, ru) - Integrate override logic in proxy forwarder for Gemini requests - Add 22 unit tests for the override logic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(gemini): address code review feedback - Use explicit else-if for disabled preference check (gemini-code-assist) - Use i18n for SelectValue placeholder instead of hardcoded string (coderabbitai) - Sync overridden body back to session.request.message for log consistency (coderabbitai) - Persist Gemini special settings immediately, matching Anthropic pattern (coderabbitai) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(gemini): use strict types for provider config and audit - Narrow preference type to "enabled" | "disabled" (exclude unreachable "inherit") - Use ProviderType and GeminiGoogleSearchPreference types instead of string Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> * fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据 (#720) * fix(api): 透传 /api/actions 认证会话以避免 getUsers 返回空数据 * fix(auth): 让 scoped session 继承 allowReadOnlyAccess 语义并支持内部降权校验 * chore: format code (dev-93585fa) * fix: bound SessionTracker active_sessions zsets by env TTL (#718) * fix(session): bound active_sessions zsets by env ttl Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(rate-limit): pass session ttl to lua cleanup Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(session): validate SESSION_TTL env and prevent ZSET leak on invalid values - Add input validation for SESSION_TTL (reject NaN, 0, negative; default 300) - Guard against invalid TTL in Lua script to prevent clearing all sessions - Use dynamic EXPIRE based on SESSION_TTL instead of hardcoded 3600s - Add unit tests for TTL validation and dynamic expiry behavior --------- Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> * fix(provider): stop standard-path fallback to legacy provider url * feat(providers): expose vendor endpoint pools in settings UI (#719) * feat(providers): add endpoint status mapping * feat(providers): add endpoint pool hover * feat(providers): show vendor endpoints in list rows * feat(providers): extract vendor endpoint CRUD table * chore(i18n): add provider endpoint UI strings * fix(providers): integrate endpoint pool into provider form * fix(provider): wrap endpoint sync in transactions to prevent race conditions (#730) * fix(provider): wrap provider create/update endpoint sync in transactions Provider create and update operations now run vendor resolution and endpoint sync inside database transactions to prevent race conditions that could leave orphaned or inconsistent endpoint rows. Key changes: - createProvider: wrap vendor + insert + endpoint seed in a single tx - updateProvider: wrap vendor + update + endpoint sync in a single tx - Add syncProviderEndpointOnProviderEdit for atomic URL/type/vendor migration with in-place update, soft-delete, and conflict handling - Vendor cleanup failures degrade to warnings instead of propagating - Add comprehensive unit and integration tests for sync edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(provider): defer endpoint circuit reset until transaction commit Avoid running endpoint circuit reset side effects inside DB transactions to prevent rollback inconsistency. Run resets only after commit and add regression tests for deferred reset behavior in helper and provider update flows. * fix(provider): distinguish noop from created-next in endpoint sync action label When ensureNextEndpointActive() returns "noop" (concurrent transaction already created an active next endpoint), the action was incorrectly labelled "kept-previous-and-created-next". Add a new "kept-previous-and-kept-next" action to ProviderEndpointSyncAction and use a three-way branch so callers and logs reflect the true outcome. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: address review comments from PR #731 - fix(auth): prevent scoped session access widening via ?? -> && guard - fix(i18n): standardize zh-CN provider terminology to "服务商" - fix(i18n): use consistent Russian translations for circuit status - fix(i18n): replace raw formatDistanceToNow with locale-aware RelativeTime - fix(gemini): log warning for unknown google search preference values - fix(error-rules): check subscribeCacheInvalidation return value - fix(test): correct endpoint hover sort test to assert URLs not labels Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: export auth session storage and fix test mock types - Export authSessionStorage from auth-session-storage.node.ts to prevent undefined on named imports; remove duplicate declare global block - Fix mockEndpoints in provider-endpoint-hover test: remove nonexistent lastOk/lastLatencyMs fields, add missing lastProbe* fields, use Date objects for createdAt/updatedAt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: miraserver <20286838+miraserver@users.noreply.github.com> Co-authored-by: John Doe <johndoe@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: 泠音 <im@ling.plus> Co-authored-by: Longlone <41830147+WAY29@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Summary
Problem
Related Issues:
Solution
Move the cost column from
ALWAYS_VISIBLE_COLUMNStoDEFAULT_VISIBLE_COLUMNS, making it toggleable while keeping it visible by default. This provides flexibility without changing the default UI for most users.Changes
Features
costtoLogsTableColumntype andDEFAULT_VISIBLE_COLUMNSRefactoring
VirtualizedLogsTableColumntype definitionLogsTableColumntype fromcolumn-visibility.tsto ensure type consistencyTests
DEFAULT_VISIBLE_COLUMNScontainscosttoggleColumn('cost')test to verify toggle functionality works correctlyFiles Changed
src/lib/column-visibility.tscostto type and default columns; removed from always-visible columnssrc/lib/column-visibility.test.tssrc/app/[locale]/dashboard/logs/_components/column-visibility-dropdown.tsxsrc/app/[locale]/dashboard/logs/_components/virtualized-logs-table.tsxVerification
logs.columns.costexists in all 5 localesDescription enhanced by Claude AI
Greptile Overview
Greptile Summary
This PR successfully implements toggleable visibility for the cost column in logs tables, addressing user needs for flat-rate subscriptions where cost is always zero.
Key changes:
costfromALWAYS_VISIBLE_COLUMNStoDEFAULT_VISIBLE_COLUMNS, making it toggleable while keeping it visible by defaultVirtualizedLogsTableColumntype definition, now using sharedLogsTableColumntype fromcolumn-visibility.tsvirtualized-logs-table.tsxcostUsdfrom truthy check to explicit null check (!= null), correctly handling zero-cost scenarioscolumn-visibility-dropdown.tsxQuality improvements:
log.costUsd != nullcheck in both table components is a bug fix that correctly displays zero costs instead of treating them as falsyThe implementation is clean, well-tested, and maintains backward compatibility by keeping cost visible by default.
Confidence Score: 5/5
Important Files Changed
costto LogsTableColumn type and DEFAULT_VISIBLE_COLUMNS, removed from ALWAYS_VISIBLE_COLUMNS - clean implementationSequence Diagram
sequenceDiagram participant User participant Dropdown as ColumnVisibilityDropdown participant Storage as localStorage participant Table as VirtualizedLogsTable User->>Dropdown: Click column visibility button Dropdown->>Storage: getHiddenColumns(userId, tableId) Storage-->>Dropdown: Return hidden columns array User->>Dropdown: Toggle "cost" column Dropdown->>Dropdown: Update hiddenColumns state Dropdown->>Storage: setHiddenColumns(userId, tableId, newHidden) Storage-->>Storage: Save to localStorage Dropdown->>Table: onVisibilityChange(newHidden) Table->>Table: Update hideCostColumn flag Table->>Table: Re-render with cost column hidden/shown Note over Table: Cost column conditionally rendered<br/>based on hiddenColumns prop User->>Dropdown: Click reset button Dropdown->>Storage: setHiddenColumns(userId, tableId, []) Storage-->>Storage: Remove from localStorage Dropdown->>Table: onVisibilityChange([]) Table->>Table: Show all columns (default state)(2/5) Greptile learns from your feedback when you react with thumbs up/down!