Skip to content

feat(gemini): add Google Search web access preference for Gemini providers#721

Merged
ding113 merged 3 commits intodevfrom
feat/gemini-google-search-preference
Feb 4, 2026
Merged

feat(gemini): add Google Search web access preference for Gemini providers#721
ding113 merged 3 commits intodevfrom
feat/gemini-google-search-preference

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Feb 4, 2026

Summary

Add provider-level preference for Gemini API providers to control Google Search (web access) tool injection with three modes: inherit (follow client request), enabled (force inject tool), and disabled (force remove tool). Includes full audit trail support for special settings display in logs.

Problem

Gemini providers need fine-grained control over the Google Search tool to:

  • Force enable web access for certain API keys regardless of client requests
  • Disable web access for API keys without grounding capabilities
  • Allow flexible provider-level policies independent of client behavior

Solution

Implements a provider override system following the same pattern as PR #689 (Anthropic overrides):

  • New geminiGoogleSearchPreference field on provider configuration
  • Runtime request transformation in proxy pipeline
  • Three preference modes:
    • inherit (default): Pass through client's request unchanged
    • enabled: Force inject {googleSearch: {}} tool if not present
    • disabled: Force remove googleSearch tool if present
  • Comprehensive audit trail for observability

Related PRs:

Changes

Core Logic

  • src/lib/gemini/provider-overrides.ts - Override implementation with audit trail support
  • src/app/v1/_lib/proxy/forwarder.ts - Integration into proxy pipeline for Gemini requests

Database

  • drizzle/0062_aromatic_taskmaster.sql - Adds nullable gemini_google_search_preference varchar(20) column

Type System & Repository

  • src/types/provider.ts + special-settings.ts - Type definitions for preference and audit
  • src/drizzle/schema.ts - Database schema
  • src/repository/provider.ts + transformers.ts - Field mappings
  • src/lib/validation/schemas.ts - Validation schema

UI & Forms

  • src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx - New "Gemini Parameter Overrides" section (visible only for gemini/gemini-cli types)
  • Form context and types updated for state management

i18n

  • Added translations for 5 languages (en, zh-CN, zh-TW, ja, ru)

Testing

  • tests/unit/lib/gemini/provider-overrides.test.ts - 22 unit tests covering all scenarios

Test Plan

  • TypeScript type check passes
  • Lint check passes
  • All 22 new unit tests pass
  • Production build succeeds
  • Manual test: Create Gemini provider with enabled preference, verify googleSearch tool is injected
  • Manual test: Create Gemini provider with disabled preference, verify googleSearch tool is removed

Database Migration

Migration 0062_aromatic_taskmaster.sql adds nullable gemini_google_search_preference varchar(20) column to providers table.

Checklist

  • Code follows project conventions
  • Self-review completed
  • Tests pass locally
  • i18n translations added (5 languages)
  • Database migration generated via bun run db:generate

Description enhanced by Claude AI

Greptile Overview

Greptile Summary

This PR adds provider-level Google Search (web access) control for Gemini API providers, following the same architecture pattern as PR #689 for Anthropic overrides.

Key Changes:

  • Implements three-mode preference system: inherit (default, pass-through), enabled (force inject googleSearch tool), disabled (force remove googleSearch tool)
  • Core override logic in provider-overrides.ts with comprehensive tool array manipulation (inject/remove) and immutable request handling
  • Integration into proxy pipeline at forwarder.ts with audit trail persistence to both session storage and message database
  • Full database migration with nullable gemini_google_search_preference column
  • Complete type system coverage across Provider interfaces, validation schemas, and repository transformers
  • UI form section conditionally visible for gemini/gemini-cli provider types with dropdown selector
  • Comprehensive test suite with 22 unit tests covering all scenarios including edge cases
  • i18n translations for 5 languages (en, zh-CN, zh-TW, ja, ru)

Architecture:
The implementation follows established patterns from the codebase (particularly Anthropic overrides), ensuring consistency in:

  • Override application with lazy cloning for performance
  • Audit trail structure with hit, action, preference, and context fields
  • Special settings deduplication via buildSettingKey
  • Form state management with dedicated reducer actions

Code Quality:

  • Clean separation of concerns with dedicated override module
  • Immutable transformations (only clones when modifications needed)
  • Proper type safety throughout the stack
  • Edge case handling (non-plain objects, empty arrays, missing provider metadata)

Confidence Score: 5/5

  • This PR is safe to merge with high confidence - well-tested, follows established patterns, and includes proper audit trails
  • The implementation demonstrates excellent code quality with comprehensive test coverage (22 tests), follows proven architecture patterns from PR feat(provider): add Anthropic max_tokens and thinking budget override settings #689, includes proper type safety, handles edge cases gracefully, and provides full audit trail support for observability
  • No files require special attention - all changes are well-structured and properly tested

Important Files Changed

Filename Overview
src/lib/gemini/provider-overrides.ts Implements Gemini Google Search tool override logic with inject/remove/passthrough actions and comprehensive audit trail support
src/app/v1/_lib/proxy/forwarder.ts Integrates Gemini override into proxy pipeline, applies transformations to request body, persists audit data to session and message records
tests/unit/lib/gemini/provider-overrides.test.ts Complete test coverage with 22 tests covering all scenarios including non-Gemini providers, inherit, enabled, disabled preferences, and edge cases
src/types/special-settings.ts Adds GeminiGoogleSearchOverrideSpecialSetting type with action, preference, and hadGoogleSearchInRequest tracking fields for audit trail
drizzle/0062_aromatic_taskmaster.sql Clean migration adding nullable gemini_google_search_preference varchar(20) column to providers table
src/lib/validation/schemas.ts Adds GEMINI_GOOGLE_SEARCH_PREFERENCE enum schema and integrates it into CreateProviderSchema and UpdateProviderSchema with inherit default

Sequence Diagram

sequenceDiagram
    participant Client
    participant ProxyForwarder
    participant ProviderOverrides as provider-overrides.ts
    participant SessionManager
    participant MessageDB as Message Database
    participant GeminiAPI as Gemini API

    Client->>ProxyForwarder: API Request with tools array
    Note over ProxyForwarder: Load provider config<br/>(geminiGoogleSearchPreference)
    
    alt Provider Type is gemini/gemini-cli
        ProxyForwarder->>ProviderOverrides: applyGeminiGoogleSearchOverrideWithAudit(provider, request)
        
        alt Preference is "inherit" or null
            ProviderOverrides-->>ProxyForwarder: {request: unchanged, audit: null}
        else Preference is "enabled"
            alt googleSearch tool not present
                ProviderOverrides->>ProviderOverrides: Inject {googleSearch: {}} into tools array
                ProviderOverrides-->>ProxyForwarder: {request: modified, audit: {action: "inject"}}
            else googleSearch already present
                ProviderOverrides-->>ProxyForwarder: {request: unchanged, audit: {action: "passthrough"}}
            end
        else Preference is "disabled"
            alt googleSearch tool present
                ProviderOverrides->>ProviderOverrides: Remove googleSearch from tools array
                ProviderOverrides-->>ProxyForwarder: {request: modified, audit: {action: "remove"}}
            else googleSearch not present
                ProviderOverrides-->>ProxyForwarder: {request: unchanged, audit: {action: "passthrough"}}
            end
        end
        
        alt audit is not null
            ProxyForwarder->>ProxyForwarder: session.addSpecialSetting(audit)
            ProxyForwarder->>SessionManager: storeSessionSpecialSettings(sessionId, specialSettings)
            ProxyForwarder->>MessageDB: updateMessageRequestDetails(messageId, {specialSettings})
        end
    else Provider Type is not gemini
        Note over ProviderOverrides: Skip override processing
    end
    
    ProxyForwarder->>ProxyForwarder: Serialize modified request body
    ProxyForwarder->>GeminiAPI: Forward request with transformed tools
    GeminiAPI-->>ProxyForwarder: Response
    ProxyForwarder-->>Client: Proxy response
Loading

…iders

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>
@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

为 Gemini 提供商新增可配置的 Google Search 覆盖偏好:添加数据库列、Drizzle 元数据、类型/验证、UI 文案与表单字段、后端覆盖与审计逻辑、special-settings 序列化扩展及单元测试。

Changes

Cohort / File(s) Summary
数据库迁移与快照
drizzle/0062_aromatic_taskmaster.sql, drizzle/meta/0062_snapshot.json, drizzle/meta/_journal.json
新增 gemini_google_search_preference 列到 providers 表并更新 Drizzle 快照与 journal(idx 62)。
类型与模式
src/types/provider.ts, src/types/special-settings.ts, src/lib/validation/schemas.ts, src/drizzle/schema.ts
新增 GeminiGoogleSearchPreference 枚举与 geminiGoogleSearchPreference 字段,并在验证 schema 与 Drizzle schema 中声明对应列与默认/可选行为。
后端覆盖与审计实现
src/lib/gemini/provider-overrides.ts, src/lib/utils/special-settings.ts, src/app/v1/_lib/proxy/forwarder.ts
新增覆盖模块以注入/移除 request 中的 googleSearch tool,提供带审计的变体;在 forwarder 中应用覆盖、记录并持久化 special setting;扩展 special-settings 的序列化键。
数据访问与转换
src/repository/provider.ts, src/repository/_shared/transformers.ts, src/actions/providers.ts
在创建/查询/更新路径中读写并透传 geminiGoogleSearchPreference,transformer 与 actions 输出包含该字段。
UI 与表单状态
messages/*/settings/providers/form/sections.json (en, ja, ru, zh-CN, zh-TW), src/app/[locale]/settings/providers/_components/forms/provider-form/...
多语言表单新增 “Gemini Parameter Overrides / Google Search” 文案;表单 state、reducer、类型与提交 payload 增加 geminiGoogleSearchPreference,并在 providerType 为 Gemini 时渲染选择控件。
单元测试
tests/unit/lib/gemini/provider-overrides.test.ts
新增全面单元测试,覆盖注入、移除、透传与审计元数据场景及边界情况。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% 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
Title check ✅ Passed 标题清晰准确地总结了主要变更:为Gemini供应商添加Google搜索网络访问偏好设置,是该PR的核心功能。
Description check ✅ Passed 描述详细说明了问题、解决方案、核心逻辑和所有受影响的文件,与PR的实际变更内容高度相关。

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/gemini-google-search-preference

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
Copy link
Contributor

Summary of Changes

Hello @ding113, 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 enhances the platform's flexibility by allowing administrators to precisely manage Google Search web access for Gemini API providers. By introducing a provider-level preference with 'inherit', 'enabled', and 'disabled' modes, it provides granular control over tool injection in Gemini requests. This feature is fully integrated across the backend, database, and frontend, complete with internationalization and detailed audit logging, empowering users to tailor Gemini's behavior to specific needs while maintaining transparency.

Highlights

  • Gemini Google Search Preference: Introduced a new provider-level setting for Gemini API providers to control Google Search (web access) tool injection.
  • Flexible Control Modes: The preference offers three modes: 'inherit' (follows client request), 'enabled' (forces injection of the Google Search tool), and 'disabled' (forces removal of the Google Search tool).
  • Audit Trail Support: Implemented comprehensive audit logging for the Google Search preference overrides, ensuring transparency in how requests are modified.
  • Full-Stack Integration: Includes database schema updates, backend logic for applying overrides, API endpoint modifications, and frontend UI for configuring the preference.
  • Internationalization (i18n): Added translations for the new UI elements in English, Simplified Chinese, Traditional Chinese, Japanese, and Russian.
  • Extensive Unit Testing: Covered all new logic with 22 dedicated unit tests to ensure correctness and robustness.
Changelog
  • drizzle/0062_aromatic_taskmaster.sql
    • Added gemini_google_search_preference column to the providers table.
  • drizzle/meta/0062_snapshot.json
    • Updated the Drizzle ORM schema snapshot to reflect the new gemini_google_search_preference column.
  • drizzle/meta/_journal.json
    • Recorded the new database migration 0062_aromatic_taskmaster.
  • messages/en/settings/providers/form/sections.json
    • Added new UI text for the 'Gemini Parameter Overrides' section, including labels and options for Google Search preference (inherit, enabled, disabled).
  • messages/ja/settings/providers/form/sections.json
    • Added Japanese translations for the new 'Gemini Parameter Overrides' UI section.
  • messages/ru/settings/providers/form/sections.json
    • Added Russian translations for the new 'Gemini Parameter Overrides' UI section.
  • messages/zh-CN/settings/providers/form/sections.json
    • Added Simplified Chinese translations for the new 'Gemini Parameter Overrides' UI section.
  • messages/zh-TW/settings/providers/form/sections.json
    • Added Traditional Chinese translations for the new 'Gemini Parameter Overrides' UI section.
  • src/actions/providers.ts
    • Modified getProviders function to include the new geminiGoogleSearchPreference when fetching provider data.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx
    • Updated the provider form submission logic to include gemini_google_search_preference in the payload.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-context.tsx
    • Initialized the geminiGoogleSearchPreference state for new providers or when editing existing ones.
    • Added a new action type SET_GEMINI_GOOGLE_SEARCH to the form reducer to handle updates to this preference.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/provider-form-types.ts
    • Imported GeminiGoogleSearchPreference type.
    • Added geminiGoogleSearchPreference to the RoutingState interface.
    • Added SET_GEMINI_GOOGLE_SEARCH to the ProviderFormAction type.
  • src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx
    • Imported GeminiGoogleSearchPreference type.
    • Added a new UI section 'Gemini Parameter Overrides' with a dropdown for 'Google Search (Web Access)' preference, visible only for Gemini provider types.
  • src/app/v1/_lib/proxy/forwarder.ts
    • Imported applyGeminiGoogleSearchOverrideWithAudit.
    • Integrated the applyGeminiGoogleSearchOverrideWithAudit function to dynamically modify the request body for Gemini providers based on the configured Google Search preference, and records audit information.
  • src/drizzle/schema.ts
    • Added geminiGoogleSearchPreference column definition to the providers table schema.
  • src/lib/gemini/provider-overrides.ts
    • New file containing core logic for applyGeminiGoogleSearchOverride and applyGeminiGoogleSearchOverrideWithAudit to manage Google Search tool injection/removal.
  • src/lib/utils/special-settings.ts
    • Extended the buildSettingKey function to handle the new gemini_google_search_override special setting for audit logging.
  • src/lib/validation/schemas.ts
    • Defined a new Zod enum GEMINI_GOOGLE_SEARCH_PREFERENCE with values 'inherit', 'enabled', 'disabled'.
    • Added gemini_google_search_preference to CreateProviderSchema and UpdateProviderSchema with a default of 'inherit'.
  • src/repository/_shared/transformers.ts
    • Updated data transformation to include geminiGoogleSearchPreference from the database to the Provider object.
  • src/repository/provider.ts
    • Modified repository functions to handle the new geminiGoogleSearchPreference field during CRUD operations.
  • src/types/provider.ts
    • Defined the GeminiGoogleSearchPreference type.
    • Added geminiGoogleSearchPreference to the Provider, ProviderDisplay, CreateProviderData, and UpdateProviderData interfaces.
  • src/types/special-settings.ts
    • Added GeminiGoogleSearchOverrideSpecialSetting to the SpecialSetting union type and defined its structure for audit logging.
  • tests/unit/lib/gemini/provider-overrides.test.ts
    • Added 22 unit tests to verify the functionality of applyGeminiGoogleSearchOverride and applyGeminiGoogleSearchOverrideWithAudit, covering various scenarios for enabled, disabled, and inherit preferences, as well as edge cases.
Activity
  • The pull request was authored by ding113.
  • It includes 22 new unit tests covering all scenarios for Gemini Google Search preference overrides.
  • Manual testing steps are outlined to verify the functionality: creating Gemini providers with enabled/disabled preferences and checking for correct Google Search tool injection/removal.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

🧪 测试结果

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

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

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 introduces a valuable feature for controlling Google Search web access on Gemini providers. The implementation is well-executed across the stack, including database changes, backend logic, frontend UI, and comprehensive unit tests. The new override logic is cleanly encapsulated and integrated. I have one minor suggestion to improve the robustness of the new logic, but overall, this is a high-quality contribution.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/[locale]/settings/providers/_components/forms/provider-form/index.tsx (1)

575-575: ⚠️ Potential issue | 🟠 Major

移除不必要的默认导出

命名导出 ProviderForm 已存在且被所有导入处使用,无需保留默认导出。直接删除第 575 行即可,无需调整任何导入语句。

建议修改
-export default ProviderForm;

符合编码规范:**/*.{ts,tsx}: 优先使用命名导出而非默认导出。

🤖 Fix all issues with AI agents
In
`@src/app/`[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx:
- Around line 659-689: Replace the hardcoded placeholder string in SelectValue
("inherit") with a translated string via the existing i18n function t, e.g. use
t("sections.routing.geminiOverrides.googleSearch.placeholder") (or another
appropriate key under sections.routing.geminiOverrides.googleSearch) instead of
the literal; update the translation resources for that key across all supported
locales (zh-CN, zh-TW, en, ja, ru) so the placeholder is localized; the change
should be made where SelectValue is used and keep SelectItem option keys
unchanged.

In `@src/app/v1/_lib/proxy/forwarder.ts`:
- Around line 1287-1297: The code updates bodyToSerialize after calling
applyGeminiGoogleSearchOverrideWithAudit but does not write the overridden
payload back to session.request.message, causing inconsistent logs/audits; when
googleSearchAudit is truthy, assign the overriddenBody back to
session.request.message (and keep session.addSpecialSetting(googleSearchAudit)
as-is) so subsequent error handling and auditing read the same request payload
that was sent; locate this change around the
applyGeminiGoogleSearchOverrideWithAudit call and session.request.message usage
to implement the assignment.
- Around line 1289-1295: The Gemini Google Search audit (returned from
applyGeminiGoogleSearchOverrideWithAudit and currently passed to
session.addSpecialSetting) is never persisted; after calling
session.addSpecialSetting(googleSearchAudit) you must mirror the other audit
flow by retrieving the updated settings with session.getSpecialSettings(), then
call SessionManager.storeSessionSpecialSettings(sessionId, specialSettings) and
finally invoke updateMessageRequestDetails(messageId, { specialSettings }) so
the audit is stored and appears in logs and message details; locate the block
around applyGeminiGoogleSearchOverrideWithAudit and augment it to perform these
three persistence calls using the same helpers (getSpecialSettings,
SessionManager.storeSessionSpecialSettings, updateMessageRequestDetails).
🧹 Nitpick comments (2)
src/types/provider.ts (1)

137-139: 补充 geminiGoogleSearchPreference 的 null/undefined 语义说明

建议在字段注释中明确:null 表示数据库空值/等同继承,undefined 表示不更新(在 Create/Update 中),以免后续误用。

Based on learnings: In TypeScript interfaces, explicitly document and enforce distinct meanings for null and undefined.

src/lib/gemini/provider-overrides.ts (1)

94-135: 考虑对无效 preference 值进行显式处理

当前实现在 Line 130 使用类型断言 preference as "enabled" | "disabled",依赖于 Line 105-107 已排除 inherit 和空值。如果传入未预期的值(如 "unknown"),该值会被直接断言为 "enabled" | "disabled" 类型,可能导致审计日志中记录不准确的信息。

建议增加运行时校验或使用更严格的类型守卫,以提高健壮性:

可选的改进方案
   // inherit or not set -> pass through unchanged, no audit
   if (!preference || preference === "inherit") {
     return { request, audit: null };
   }
+
+  // Guard against unexpected preference values
+  if (preference !== "enabled" && preference !== "disabled") {
+    return { request, audit: null };
+  }

   const existingTools = Array.isArray(request.tools) ? request.tools : [];
   const hadGoogleSearch = hasGoogleSearchTool(existingTools);

这样 preference 在后续代码中会被 TypeScript 自动收窄为 "enabled" | "disabled",无需类型断言。

disabled={state.ui.isPending}
>
<SelectTrigger className="w-full">
<SelectValue placeholder="inherit" />
Copy link
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] [STANDARD-VIOLATION] Hardcoded placeholder bypasses i18n

Why this is a problem: CLAUDE.md rule: "i18n Required - All user-facing strings must use i18n (5 languages supported). Never hardcode display text". The Select placeholder is user-facing and will not be translated.

Suggested fix:

<SelectValue placeholder={t("sections.routing.geminiOverrides.googleSearch.options.inherit")} />

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR adds a Gemini provider-level Google Search override and threads it through DB schema, validation, proxy forwarding, UI settings, and unit tests. The scope is very large for a single change, which increases review/rollback risk and makes it harder to isolate regressions.

PR Size: XL

  • Lines changed: 3617
  • Files changed: 23
  • Split suggestion (XL): Consider splitting into (1) DB schema + types + validation, (2) UI + i18n messages, (3) proxy forwarder behavior + audit + tests.

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

Critical Issues (Must Fix)

None.

High Priority Issues (Should Fix)

  1. src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx:679 [MEDIUM] Hardcoded Select placeholder bypasses i18n (CLAUDE.md: "i18n Required - All user-facing strings must use i18n (5 languages supported). Never hardcode display text").

Review Coverage

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

Automated review by Codex AI

- 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>
Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR adds a Gemini provider-level Google Search override and threads it through DB schema, validation, proxy forwarding, UI settings, and unit tests. The scope is very large for a single change, which increases review/rollback risk and makes it harder to isolate regressions.

PR Size: XL

  • Lines changed: 3617
  • Files changed: 23
  • Split suggestion (XL): Consider splitting into (1) DB schema + types + validation, (2) UI + i18n messages, (3) proxy forwarder behavior + audit + tests.

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 1 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

Critical Issues (Must Fix)

None.

High Priority Issues (Should Fix)

  1. src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx:679 [MEDIUM] Hardcoded Select placeholder bypasses i18n (CLAUDE.md: "i18n Required - All user-facing strings must use i18n (5 languages supported). Never hardcode display text").

Review Coverage

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

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

  • Identified open PR #721 and applied label size/XL (3617 lines changed, 23 files).
  • Posted 1 inline review comment on a diff line:
    • src/app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx:679 — hardcoded placeholder="inherit" violates CLAUDE.md i18n rule; suggested placeholder={t("sections.routing.geminiOverrides.googleSearch.options.inherit")}.
  • Submitted the required PR review summary comment (including XL split suggestion).

@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

🧪 测试结果

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

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

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

providerId: number | null;
providerName: string | null;
action: "inject" | "remove" | "passthrough";
preference: "enabled" | "disabled" | "inherit";
Copy link

Choose a reason for hiding this comment

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

preference field includes "inherit" but this value never generates an audit entry (see provider-overrides.ts:104-106). Consider narrowing the type to "enabled" | "disabled" to match runtime behavior.

Suggested change
preference: "enabled" | "disabled" | "inherit";
preference: "enabled" | "disabled";
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/types/special-settings.ts
Line: 172:172

Comment:
`preference` field includes "inherit" but this value never generates an audit entry (see `provider-overrides.ts:104-106`). Consider narrowing the type to `"enabled" | "disabled"` to match runtime behavior.

```suggestion
  preference: "enabled" | "disabled";
```

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

id?: number;
name?: string;
providerType?: string;
geminiGoogleSearchPreference?: string | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

[HIGH] [TYPE-WEAK-INVARIANT] Type definition uses weak string types instead of strict enums

Why this is a problem: The GeminiProviderOverrideConfig type uses string for providerType and geminiGoogleSearchPreference, which allows invalid values to pass through at compile time. While runtime validation exists in schemas.ts, the type system should enforce correctness earlier.

Suggested fix:

import type { ProviderType } from "@/types/provider";
import type { GeminiGoogleSearchPreference } from "@/types/provider";

type GeminiProviderOverrideConfig = {
  id?: number;
  name?: string;
  providerType?: ProviderType;  // Instead of string
  geminiGoogleSearchPreference?: GeminiGoogleSearchPreference | null;  // Instead of string | null
};

delete output.tools;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] [ERROR-NO-USER-FEEDBACK] Invalid preference values are silently ignored without logging

Why this is a problem: If an invalid preference value (not 'enabled', 'disabled', or 'inherit') somehow bypasses validation and reaches this code, it will be silently ignored with no audit trail. This makes debugging configuration issues difficult.

Suggested fix:

if (preference === "enabled") {
  // Force inject googleSearch tool if not present
  if (!hadGoogleSearch) {
    ensureCloned();
    output.tools = [...existingTools, { googleSearch: {} }];
  }
} else if (preference === "disabled") {
  // Force remove googleSearch tool if present
  if (hadGoogleSearch) {
    ensureCloned();
    const filteredTools = removeGoogleSearchTools(existingTools);
    if (filteredTools.length > 0) {
      output.tools = filteredTools;
    } else {
      delete output.tools;
    }
  }
} else {
  // Log unexpected preference values for debugging
  logger.warn("[GeminiOverride] Unexpected preference value, treating as inherit", {
    preference,
    providerId: provider.id,
    providerName: provider.name,
  });
}

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR implements a provider-level Google Search preference override for Gemini API providers. The implementation follows established patterns (similar to PR #689 for Anthropic overrides), includes comprehensive test coverage (22 unit tests), and properly integrates with the existing audit trail system.

PR Size: XL

  • Lines changed: 3,647 (3,645 additions, 2 deletions)
  • Files changed: 23

Size recommendation: This PR is appropriately scoped as a single feature. The bulk of changes come from generated migration files and i18n translations (5 languages), which cannot be split. The core logic is well-contained in a few key files.

Issues Found

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

High Priority Issues (Should Fix)

  1. Type Safety (src/lib/gemini/provider-overrides.ts:3-7): GeminiProviderOverrideConfig uses weak string types instead of strict ProviderType and GeminiGoogleSearchPreference enums, allowing invalid values to pass compile-time checks.

Medium Priority Issues (Consider Fixing)

  1. Error Handling (src/lib/gemini/provider-overrides.ts:68-86): Invalid preference values are silently ignored without logging, making configuration debugging difficult if values bypass validation.

Strengths

  • Consistent Architecture: Follows the same pattern as existing Anthropic/Codex overrides
  • Comprehensive Testing: 22 unit tests covering all scenarios including edge cases
  • Proper Integration: Correctly integrates with proxy pipeline and audit trail
  • Complete i18n: Full translations for all 5 supported languages
  • Clean Migration: Properly generated database migration using bun run db:generate
  • Good Code Organization: Lazy cloning pattern, proper type guards, and defensive programming

Review Coverage

  • Logic and correctness - Clean
  • Security (OWASP Top 10) - Clean
  • Error handling - 1 Medium issue
  • Type safety - 1 High issue
  • Documentation accuracy - Clean
  • Test coverage - Excellent (22 tests)
  • Code clarity - Good

Validation Notes

I verified that:

  • The override logic is called within the existing try-catch blocks in forwarder.ts (lines 242-502)
  • Errors are properly logged via .catch() handlers (lines 1304-1309, 1314-1319)
  • The function itself cannot throw exceptions (pure transformation logic)
  • Global error handling exists at the proxy layer
  • Zod validation in schemas.ts catches invalid values at API boundaries

The identified issues are minor improvements rather than critical defects. The implementation is production-ready with the suggested enhancements.


Automated review by Claude AI

- 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>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 4, 2026

🧪 测试结果

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

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

@ding113 ding113 merged commit 5f9deca into dev Feb 4, 2026
9 of 10 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Feb 4, 2026
@github-actions github-actions bot mentioned this pull request Feb 7, 2026
10 tasks
ding113 added a commit that referenced this pull request Feb 7, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant