feat(gemini): add Google Search web access preference for Gemini providers#721
feat(gemini): add Google Search web access preference for Gemini providers#721
Conversation
…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>
📝 WalkthroughWalkthrough为 Gemini 提供商新增可配置的 Google Search 覆盖偏好:添加数据库列、Drizzle 元数据、类型/验证、UI 文案与表单字段、后端覆盖与审计逻辑、special-settings 序列化扩展及单元测试。 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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 @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
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 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.
There was a problem hiding this comment.
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",无需类型断言。
...app/[locale]/settings/providers/_components/forms/provider-form/sections/routing-section.tsx
Show resolved
Hide resolved
| disabled={state.ui.isPending} | ||
| > | ||
| <SelectTrigger className="w-full"> | ||
| <SelectValue placeholder="inherit" /> |
There was a problem hiding this comment.
[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")} />There was a problem hiding this comment.
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)
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>
There was a problem hiding this comment.
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)
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
There was a problem hiding this comment.
- Identified open PR
#721and applied labelsize/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— hardcodedplaceholder="inherit"violates CLAUDE.md i18n rule; suggestedplaceholder={t("sections.routing.geminiOverrides.googleSearch.options.inherit")}.
- Submitted the required PR review summary comment (including XL split suggestion).
🧪 测试结果
总体结果: ✅ 所有测试通过 |
src/types/special-settings.ts
Outdated
| providerId: number | null; | ||
| providerName: string | null; | ||
| action: "inject" | "remove" | "passthrough"; | ||
| preference: "enabled" | "disabled" | "inherit"; |
There was a problem hiding this 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.
| 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.
src/lib/gemini/provider-overrides.ts
Outdated
| id?: number; | ||
| name?: string; | ||
| providerType?: string; | ||
| geminiGoogleSearchPreference?: string | null; |
There was a problem hiding this comment.
[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; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
[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,
});
}There was a problem hiding this comment.
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)
- Type Safety (src/lib/gemini/provider-overrides.ts:3-7):
GeminiProviderOverrideConfiguses weakstringtypes instead of strictProviderTypeandGeminiGoogleSearchPreferenceenums, allowing invalid values to pass compile-time checks.
Medium Priority Issues (Consider Fixing)
- 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>
🧪 测试结果
总体结果: ✅ 所有测试通过 |
* 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
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), anddisabled(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:
Solution
Implements a provider override system following the same pattern as PR #689 (Anthropic overrides):
geminiGoogleSearchPreferencefield on provider configurationinherit(default): Pass through client's request unchangedenabled: Force inject{googleSearch: {}}tool if not presentdisabled: Force removegoogleSearchtool if presentRelated PRs:
Changes
Core Logic
src/lib/gemini/provider-overrides.ts- Override implementation with audit trail supportsrc/app/v1/_lib/proxy/forwarder.ts- Integration into proxy pipeline for Gemini requestsDatabase
drizzle/0062_aromatic_taskmaster.sql- Adds nullablegemini_google_search_preferencevarchar(20) columnType System & Repository
src/types/provider.ts+special-settings.ts- Type definitions for preference and auditsrc/drizzle/schema.ts- Database schemasrc/repository/provider.ts+transformers.ts- Field mappingssrc/lib/validation/schemas.ts- Validation schemaUI & 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)i18n
Testing
tests/unit/lib/gemini/provider-overrides.test.ts- 22 unit tests covering all scenariosTest Plan
enabledpreference, verify googleSearch tool is injecteddisabledpreference, verify googleSearch tool is removedDatabase Migration
Migration
0062_aromatic_taskmaster.sqladds nullablegemini_google_search_preferencevarchar(20) column toproviderstable.Checklist
bun run db:generateDescription 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:
inherit(default, pass-through),enabled(force inject googleSearch tool),disabled(force remove googleSearch tool)provider-overrides.tswith comprehensive tool array manipulation (inject/remove) and immutable request handlingforwarder.tswith audit trail persistence to both session storage and message databasegemini_google_search_preferencecolumnArchitecture:
The implementation follows established patterns from the codebase (particularly Anthropic overrides), ensuring consistency in:
hit,action,preference, and context fieldsbuildSettingKeyCode Quality:
Confidence Score: 5/5
Important Files Changed
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