Skip to content

refactor: remover todos providers de IA exceto Google Gemini - #48

Open
thaleslaray wants to merge 4 commits into
mainfrom
refactor/gemini-only
Open

refactor: remover todos providers de IA exceto Google Gemini#48
thaleslaray wants to merge 4 commits into
mainfrom
refactor/gemini-only

Conversation

@thaleslaray

@thaleslaray thaleslaray commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Summary

  • Remove 100% do suporte a OpenAI, Voyage, Cohere e Together AI — apenas Google Gemini permanece
  • Simplifica AIProvider e EmbeddingProvider para tipo literal 'google'
  • Remove openai_api_key do Supabase settings, da config, dos endpoints e da UI
  • Corrige bug visual: seção "Modelo principal" duplicada em /settings/ai
  • Helicone Observability agora oculto fora do dev mode
  • Corrige contadores de contatos limitados a 1000 (regressão do PostgREST default limit)

Arquivos principais

Arquivo Mudança
lib/ai/providers.ts AIProvider = 'google' (era union com openai)
lib/ai/ai-center-defaults.ts Removido openaiApiKey de AiDirectConfig
lib/ai/embeddings.ts EmbeddingProvider = 'google', removidos OpenAI/Voyage/Cohere
lib/ai/unified-ai-service.ts Removido branch OpenAI em createModelInstance()
lib/ai/agents/chat-agent.ts Substituído multi-branch por Google-only
app/api/ai/models/route.ts Removido fetchOpenAIModels, sem param ?provider=
lib/supabase-db.ts contactDb.getStats() usa count: 'exact', head: true em paralelo
app/(dashboard)/settings/ai/page.tsx Removida seção duplicada + Helicone só em dev mode

Verificação

  • tsc --noEmit — 0 erros
  • ✅ 3810 testes unitários — 0 falhas
  • ✅ Stress test: 2000 calls Gemini (100 conversas × 20 turnos) — 100% sucesso, 17.7 turnos/s

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Breaking Changes
    • Consolidated AI provider support to Google Gemini exclusively. Removed support for OpenAI, Voyage, and Cohere providers.
    • Removed provider and embedding provider selection interfaces from AI settings. Configuration is now simplified with Google Gemini as the sole option.

thaleslaray and others added 4 commits April 9, 2026 13:24
- Remove OpenAI, Voyage, Cohere e Together de toda a stack de IA
- AIProvider, AiProviderType, EmbeddingProvider agora são literalmente 'google'
- unified-ai-service: remove branch OpenAI do createModelInstance
- ai-center-config: remove openai_api_key de SETTINGS_KEYS e fetch
- ai/models route: simplifica para buscar só modelos Google
- llm-providers e embedding-providers: remove providers não-Google
- settings routes: remove handling de openai_api_key
- AIGatewayPanel, AIAgentForm, settings/ai/page: remove UI de seleção de provider
- scripts/ai-quality-suite: remove comparação com OpenAI embeddings

-533 linhas removidas em 26 arquivos. TypeScript: 0 erros. Testes: 3810/3810.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
O AIGatewayPanel já tem o seletor de modelo inline no card do provider.
A seção separada 'Modelo principal' abaixo era duplicata — removida.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A RPC get_contact_stats estava retornando no máximo 1000 por causa do
limite padrão do PostgREST. Substituído por 3 queries paralelas com
count: 'exact' + head: true — o mesmo mecanismo que a listagem usa
e que já retorna o count correto (3640+) via header HTTP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Apr 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
smartzap Ready Ready Preview, Comment Apr 9, 2026 4:56pm

@coderabbitai

coderabbitai Bot commented Apr 9, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request removes OpenAI, Voyage, and Cohere provider support across the codebase, consolidating all AI functionality to use Google Gemini exclusively. Provider selection UI, conditional model instantiation logic, and multi-provider API key management are eliminated.

Changes

Cohort / File(s) Summary
Settings AI API Routes
app/api/settings/ai/route.ts, app/api/settings/all/route.ts
Removed openai_api_key handling, validation, and persistence. Updated GET to exclude OpenAI from returned config; updated POST to remove OpenAI input acceptance; updated DELETE to restrict to google provider only. fetchAISettings() now derives AI configuration solely from Google key presence.
AI Agent Direct Routes
app/api/ai-agents/[id]/chat/route.ts, app/api/ai-agents/[id]/test/route.ts
Removed createOpenAI imports and conditional provider branching. Routes now always validate config.googleApiKey and construct models via createGoogleGenerativeAI, eliminating OpenAI-specific key checks and fallback paths.
AI Embedding Provider Routes
app/api/ai-agents/embedding-providers/route.ts, app/api/ai-agents/knowledge/route.ts, app/api/ai-agents/llm-providers/route.ts
Removed openai, voyage, and cohere entries from EMBEDDING_API_KEY_MAP and LLM_API_KEY_MAP. Provider availability logic now only includes Google, simplifying mapping lookups.
AI Models API Route
app/api/ai/models/route.ts
Removed OpenAI model fetching, regex helpers, and provider query parameter parsing. Narrowed AIModelInfo.provider type from 'google' | 'openai' to 'google'. Endpoint now returns only Google Gemini models using google_api_key.
Inbox AI Routes
app/api/inbox/chat/route.ts, app/api/inbox/suggest/route.ts
Removed provider-based model selection logic and createOpenAI imports. Routes now always require config.googleApiKey and construct models unconditionally via createGoogleGenerativeAI.
Settings Page & Components
app/(dashboard)/settings/ai/page.tsx, components/features/settings/AIGatewayPanel.tsx, components/features/settings/ai-agents/AIAgentForm.tsx
Removed provider selection UI, embedding provider dropdowns, and associated loading states. AIGatewayPanel no longer tracks activeProvider state. AIAgentForm header always displays "Google Gemini". Helicone observability panel now conditionally renders in dev mode only.
Settings Type Definitions
components/features/settings/types.ts, types.ts
Narrowed AiProvider and EmbeddingProvider union types to include only 'google'. Removed openai and anthropic from AISettingsInfo.providers object type. Updated AIAgent.embedding_provider constraint accordingly.
AI Configuration & Defaults
lib/ai/ai-center-config.ts, lib/ai/ai-center-defaults.ts, lib/ai/providers.ts
Removed openaiApiKey from configuration flow and AiDirectConfig type. AiProviderType union narrowed to 'google'. AI_PROVIDERS constant no longer includes OpenAI provider entry or models. normalizeDirect now always sets provider to DEFAULT_AI_DIRECT.provider.
AI Embedding & Model Services
lib/ai/embeddings.ts, lib/ai/services/ai-judge.ts, lib/ai/services/template-agent.ts, lib/ai/unified-ai-service.ts
Removed createOpenAI imports and conditional provider logic. EmbeddingProvider type narrowed to 'google'. All services now always construct Google Gemini models via createGoogleGenerativeAI, validating only googleApiKey. AI Gateway routing logic removed.
Direct Agent Implementation
lib/ai/agents/chat-agent.ts
Removed createOpenAI import and provider branching. Service now requires only directConfig.googleApiKey and always constructs Google Gemini model. Log messages updated to always report Using google/<modelId>.
Settings Service & Hooks
services/settingsService.ts, hooks/useSettingsAI.ts
saveAIConfig no longer accepts provider or openai_api_key parameters. removeAIKey parameter type narrowed to 'google' and always calls DELETE with hardcoded provider=google. Hook no longer maintains OpenAI state (openaiKey, openaiKeyDraft) or exposes related handlers.
MCP Tools
lib/mcp/tools/settings.ts
sz.settings.set_ai tool removed provider and openai_api_key from input schema and handler signature. sz.settings.remove_ai_key provider input restricted to google only. Tool descriptions narrowed to Google-focused language.
Database Utilities
lib/supabase-db.ts
Replaced RPC-based contactDb.getStats with three parallel Supabase count queries against the contacts table, improving performance and reducing server-side RPC dependency.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 One path now shines through the garden bright,
Google Gemini, our single delight!
No branching trails, no provider's choice—
Just gemini whispers, in one voice.
Hop forward, code, all clean and right! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: removing all AI providers except Google Gemini across the entire codebase, which is consistently reflected in the detailed file summaries.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/gemini-only

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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6e039c78c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +50 to 54
// Sempre usa google — único provider suportado
const provider: AiProviderType = DEFAULT_AI_DIRECT.provider

const model =
typeof input?.model === 'string' && input.model.trim()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Normalize model when coercing provider to Google

This normalization now forces provider to Google but keeps any previously saved input.model, so an existing ai_direct like {"provider":"openai","model":"gpt-4o"} is converted into an invalid Google config instead of being reset. Downstream callers (e.g. chat/inbox/template services) will try to instantiate Gemini with a non-Gemini model ID and fail at runtime until someone manually edits settings. When provider is coerced, the model should also be validated against supported Google IDs (or reset to DEFAULT_AI_DIRECT.model).

Useful? React with 👍 / 👎.

Comment on lines 46 to 48
const EMBEDDING_API_KEY_MAP: Record<EmbeddingProvider, { settingKey: string; envVar: string }> = {
google: { settingKey: 'google_api_key', envVar: 'GOOGLE_GENERATIVE_AI_API_KEY' },
openai: { settingKey: 'openai_api_key', envVar: 'OPENAI_API_KEY' },
voyage: { settingKey: 'voyage_api_key', envVar: 'VOYAGE_API_KEY' },
cohere: { settingKey: 'cohere_api_key', envVar: 'COHERE_API_KEY' },
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard legacy embedding providers before map lookup

EMBEDDING_API_KEY_MAP now only has google, but this route still reads agent.embedding_provider from the database and later dereferences config.settingKey/config.envVar without a fallback. Any pre-existing agent with openai, voyage, or cohere in embedding_provider will make config undefined and crash KB-enabled chat requests. Add a fallback/migration path for legacy provider values (the same pattern is present in [id]/test).

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (7)
app/api/ai/models/route.ts (1)

77-95: ⚠️ Potential issue | 🟠 Major

Authenticate this route before using the stored Google key.

GET currently lets any caller hit /api/ai/models and spend the tenant's saved Google quota to enumerate models. Add verifyApiKey() before reading settings or calling Google. As per coding guidelines, "Enforce authentication per-route via verifyApiKey() from lib/auth.ts in API routes (no middleware.ts)".

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

In `@app/api/ai/models/route.ts` around lines 77 - 95, The GET handler currently
reads the tenant Google key and calls fetchGoogleModels without authenticating;
before calling getSettingValue or fetchGoogleModels, call and await
verifyApiKey() (importing it from lib/auth.ts) and return an appropriate
unauthorized response if it throws or returns a falsy value. Update the GET
function to first run verifyApiKey(), then proceed to call
getSettingValue('google_api_key') and fetchGoogleModels(apiKey) only after
successful verification, and ensure error handling still returns
NextResponse.json with status 502 for fetch errors and a 401/403 NextResponse
for auth failures.
app/api/ai-agents/[id]/test/route.ts (1)

23-25: ⚠️ Potential issue | 🔴 Critical

Same schema mismatch issue as chat route.

This route has the same EMBEDDING_API_KEY_MAP limitation. The lookup at line 190 will fail for agents with non-Google embedding_provider values stored in the database.

The fix recommended for app/api/ai-agents/[id]/chat/route.ts (updating the agent creation schema in app/api/ai-agents/route.ts) will resolve this for both routes.

Also applies to: 189-190

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

In `@app/api/ai-agents/`[id]/test/route.ts around lines 23 - 25,
EMBEDDING_API_KEY_MAP in app/api/ai-agents/[id]/test/route.ts only contains a
google entry so lookups for agents whose embedding_provider is not "google" will
fail; fix by (a) updating the agent creation schema in
app/api/ai-agents/route.ts to ensure embedding_provider is validated/defaulted
to supported providers (matching DB values used elsewhere) and (b) making
EMBEDDING_API_KEY_MAP and the lookup in test/route.ts robust: either add entries
for all supported providers or guard the lookup (e.g., fall back to a safe
default or throw a clear error) so getEmbeddingApiKey(embedding_provider) cannot
return undefined for valid DB values. Ensure you update the symbols
EMBEDDING_API_KEY_MAP and the agent creation schema (where embedding_provider is
defined) so both routes (chat and test) handle non-google providers
consistently.
lib/ai/ai-center-defaults.ts (1)

14-29: ⚠️ Potential issue | 🟡 Minor

Remove dead references to openaiApiKey in lib/mcp/tools/system.ts.

The destructuring of openaiApiKey on line 74 and the hasOpenAIKey check on line 78 reference a property that no longer exists in the AiDirectConfig type. These will always be undefined/false and should be removed along with their corresponding destructuring assignments.

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

In `@lib/ai/ai-center-defaults.ts` around lines 14 - 29, Remove the stale
openaiApiKey handling: delete the destructuring that pulls openaiApiKey and any
derived boolean like hasOpenAIKey in the module (e.g., the openaiApiKey variable
and hasOpenAIKey check in system.ts), and remove any conditional branches that
only run when hasOpenAIKey is true; ensure all remaining logic uses the current
AiDirectConfig fields (like googleApiKey) and that no other references to
openaiApiKey or hasOpenAIKey remain.
app/api/ai-agents/[id]/chat/route.ts (1)

46-48: ⚠️ Potential issue | 🔴 Critical

Critical: Schema validation mismatch will cause runtime errors.

The embedding_provider schemas in app/api/ai-agents/route.ts (line 32) and app/api/ai-agents/[id]/route.ts (line 32) accept ['google', 'openai', 'voyage', 'cohere'], but:

  1. The EmbeddingProvider type in lib/ai/embeddings.ts is restricted to only 'google'
  2. EMBEDDING_API_KEY_MAP in the chat route (lines 46-48) only contains google

If an agent with a non-Google embedding_provider exists in the database, line 281's lookup will return undefined, and line 286 will throw Cannot read properties of undefined (reading 'settingKey').

Update both schemas to match the actual EmbeddingProvider type:

-  embedding_provider: z.enum(['google', 'openai', 'voyage', 'cohere']).default('google'),
+  embedding_provider: z.enum(['google']).default('google'),

Also update the optional variant in the update endpoint:

-  embedding_provider: z.enum(['google', 'openai', 'voyage', 'cohere']).optional(),
+  embedding_provider: z.enum(['google']).optional(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/api/ai-agents/`[id]/chat/route.ts around lines 46 - 48, The schema enum
for embedding_provider in app/api/ai-agents/route.ts and
app/api/ai-agents/[id]/route.ts must match the actual EmbeddingProvider type
(lib/ai/embeddings.ts) and the chat logic: change the allowed values from
['google','openai','voyage','cohere'] to only ['google'] and update the
update-endpoint variant to the optional form (e.g., use the
z.enum(['google']).optional() or equivalent) so that validation matches the
EmbeddingProvider type and EMBEDDING_API_KEY_MAP lookups won't return undefined.
lib/mcp/tools/settings.ts (1)

81-103: ⚠️ Potential issue | 🟠 Major

sz.settings.set_ai still sends the pre-refactor route keys.

This tool posts inbox_suggest / inbox_chat / template_generation / ocr, but the backend now normalizes AI routes through prepareAiRoutesUpdate() into generateUtilityTemplates and generateFlowForm. The call will succeed while silently dropping the requested route changes.

Suggested contract alignment
       inputSchema: {
         model: z.string().optional().describe('Model ID (ex: gemini-2.5-flash)'),
         google_api_key: z.string().optional().describe('Chave API do Google Gemini'),
         routes: z
           .object({
-            inbox_suggest: z.boolean().optional(),
-            inbox_chat: z.boolean().optional(),
-            template_generation: z.boolean().optional(),
-            ocr: z.boolean().optional(),
+            generateUtilityTemplates: z.boolean().optional(),
+            generateFlowForm: z.boolean().optional(),
           })
           .optional()
           .describe('Rotas que usam IA (habilitar/desabilitar individualmente)'),
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/mcp/tools/settings.ts` around lines 81 - 103, The tool sz.settings.set_ai
currently sends the old route keys (inbox_suggest, inbox_chat,
template_generation, ocr) which the backend silently drops; fix this by
normalizing the routes before sending—import and call
prepareAiRoutesUpdate(routes) (or otherwise map the incoming routes to the
backend's expected keys like generateUtilityTemplates/generateFlowForm) and
include the normalized result in the POST body instead of the raw routes
variable; update the async handler in settings.ts to use the output of
prepareAiRoutesUpdate when building JSON.stringify({ model, google_api_key,
routes: normalizedRoutes }).
app/api/settings/ai/route.ts (2)

58-119: ⚠️ Potential issue | 🔴 Critical

Authenticate /api/settings/ai before exposing or mutating AI config.

GET, POST, and DELETE still execute without verifyApiKey(). That leaves key-state metadata readable and AI settings / API keys writable by any caller that can hit the route, which is too much surface area for a settings endpoint.

Based on learnings: Enforce authentication per-route via verifyApiKey() from lib/auth.ts in API routes (no middleware.ts).

Also applies to: 126-220, 227-263

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

In `@app/api/settings/ai/route.ts` around lines 58 - 119, This route exposes AI
settings without auth; import and call verifyApiKey() from lib/auth.ts at the
start of each handler (GET, POST, DELETE) in this file and return an
unauthorized NextResponse if verification fails; ensure the call is awaited
(e.g., const ok = await verifyApiKey(req) or wrap in try/catch if verifyApiKey
throws) so no settings logic (prepareAiDirectUpdate, prepareAiRoutesUpdate,
etc.) runs for unauthenticated requests.

126-196: ⚠️ Potential issue | 🟠 Major

Validate the POST payload before merging it into persisted settings.

routes, prompts, and ocr_gemini_model are accepted as arbitrary JSON and then written into settings. A malformed payload here can poison stored config and break every downstream reader that assumes the normalized shapes.

Based on learnings: In API route files under app/api/**/route.ts, use Zod validation only for user input (request body, query params, path params).

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

In `@app/api/settings/ai/route.ts` around lines 126 - 196, The POST handler
accepts arbitrary JSON for routes, prompts, and ocr_gemini_model and merges it
into persisted settings which can corrupt downstream readers; add explicit Zod
validation in the POST function to parse and validate the incoming body
(provider, model, google_api_key, routes, prompts, ocr_gemini_model) before any
merges, rejecting invalid payloads with a 400; for routes and prompts validate
against the existing normalized shapes used by prepareAiRoutesUpdate and the
prompts normalization (utilityGenerationTemplate, utilityJudgeTemplate,
flowFormTemplate and optional strategyMarketing/strategyUtility/strategyBypass)
and only write validated fields to updates, and validate ocr_gemini_model
against an allowed string/enum schema before pushing key 'ocr_gemini_model' to
updates.
🧹 Nitpick comments (5)
lib/ai/services/ai-judge.ts (1)

30-32: Honor options.apiKey or remove it from the public contract.

The new Google-only path ignores JudgeOptions.apiKey, so callers that pass an explicit key will still fail whenever the DB setting is empty. Either prefer options.apiKey ?? config.googleApiKey here or delete apiKey from JudgeOptions to avoid a broken override.

Suggested fix
-    if (!config.googleApiKey) throw new Error('Chave Google não configurada. Acesse Configurações → IA.')
-    const google = createGoogleGenerativeAI({ apiKey: config.googleApiKey })
+    const apiKey = options.apiKey || config.googleApiKey
+    if (!apiKey) throw new Error('Chave Google não configurada. Acesse Configurações → IA.')
+    const google = createGoogleGenerativeAI({ apiKey })
     const model = google(targetModelId)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ai/services/ai-judge.ts` around lines 30 - 32, The Google-only path in
ai-judge.ts ignores the caller-supplied key in JudgeOptions, so update the
Google client creation to prefer options.apiKey over the DB setting (use
options.apiKey ?? config.googleApiKey when calling createGoogleGenerativeAI) and
pass that value into google(targetModelId); alternatively remove apiKey from
JudgeOptions if intentional. Locate the code around createGoogleGenerativeAI,
the google variable and the model assignment (google(targetModelId)) and apply
the key preference or remove the option from the public contract accordingly.
app/api/ai/models/route.ts (1)

87-93: Expose the provider failure in details.

This catch block returns only { error }, so clients following the repository-wide API convention lose the actual Google error text. Return details: message here as well.

Suggested fix
     return NextResponse.json(
-      { error: `Falha ao buscar modelos: ${message}` },
+      { error: 'Falha ao buscar modelos', details: message },
       { status: 502 }
     )

Based on learnings: "In this repository, API error responses should return { details: error.message } ... Ensure catch blocks map the error to a response with details set to error.message."

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

In `@app/api/ai/models/route.ts` around lines 87 - 93, The catch block in route.ts
currently returns only { error } losing the provider error text; update the
error response inside the catch (where message is derived and NextResponse.json
is returned) to include details: message (i.e., return { error: `Falha ao buscar
modelos: ${message}`, details: message } with the existing status 502) so
clients following the repository-wide API convention receive the provider
failure text.
app/(dashboard)/settings/ai/page.tsx (1)

481-502: Unused variables from hook destructuring.

After removing the provider/model selection UI, the following destructured values appear unused: models, modelsLoading, and fetchModels. Consider removing them to reduce confusion:

 const {
   isDevMode,
   provider,
   model,
-  models,
-  modelsLoading,
   routes,
   prompts,
   // ...
-  fetchModels,
 } = useSettingsAIController()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(dashboard)/settings/ai/page.tsx around lines 481 - 502, The
destructuring of useSettingsAIController includes unused values models,
modelsLoading, and fetchModels; remove these three identifiers from the
destructuring assignment in page.tsx and also remove them from the return object
of useSettingsAIController (or cease returning them) so the hook signature and
its consumers stay consistent (refer to useSettingsAIController and the
destructured list including
isDevMode/provider/model/.../handleStrategiesToggle).
lib/ai/ai-center-defaults.ts (1)

25-25: Stale comment example.

The comment mentions 'gpt-5.4' as an example model ID, but OpenAI support has been removed. Consider updating to only show Gemini examples:

-  /** Model ID no formato bare, sem prefixo de provider (ex: 'gemini-2.5-flash', 'gpt-5.4'). */
+  /** Model ID no formato bare, sem prefixo de provider (ex: 'gemini-2.5-flash'). */
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ai/ai-center-defaults.ts` at line 25, Update the stale JSDoc comment that
currently reads "Model ID no formato bare, sem prefixo de provider (ex:
'gemini-2.5-flash', 'gpt-5.4')" to remove the OpenAI example and only show valid
Gemini examples; locate the comment in lib/ai/ai-center-defaults.ts (the "Model
ID no formato bare..." JSDoc above the model ID declaration) and replace the
example list so it only includes Gemini model IDs (e.g., 'gemini-2.5-flash') and
any other supported provider examples if applicable.
components/features/settings/AIGatewayPanel.tsx (1)

60-183: This component is acting as a second AI-settings controller.

It now duplicates loadConfig, key save/remove, and model-fetch flows that already exist in hooks/useSettingsAI.ts. That makes components/features/settings/AIGatewayPanel.tsx and the hook two separate API clients for the same feature, which will drift quickly.

As per coding guidelines: components/features/**/*.tsx: Component files in components/features/ should be pure presentational components with typed props interfaces.

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

In `@components/features/settings/AIGatewayPanel.tsx` around lines 60 - 183, This
component duplicates API/client logic (loadConfig, fetchModels, handleSaveKey,
handleRemoveKey, handleSelectModel) that already lives in
hooks/useSettingsAI.ts; refactor AIGatewayPanel to be a pure presentational
component that consumes the hook instead of re-implementing it: remove the
internal loadConfig, fetchModels, handleSaveKey, handleRemoveKey,
handleSelectModel and providerState management, import and call useSettingsAI to
get state (providerState, activeModel, loading, saving) and handlers
(loadConfig/fetchModels/saveKey/removeKey/selectModel or similarly named
functions exported by the hook), wire those hook values/handlers into the JSX
props/controls, and ensure any local UI-only state (e.g., showKey, modelSearch)
remains minimal or is lifted into typed props; keep component file purely
presentational with typed props if needed and rely on unique symbols loadConfig,
fetchModels, handleSaveKey, handleRemoveKey, handleSelectModel and the hook
useSettingsAI for the remote logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/ai/ai-center-config.ts`:
- Around line 132-139: The getAiDirectConfig() path currently reads only
Supabase keys and a legacy 'gemini_api_key' row; restore the
environment-variable fallback by checking
process.env.GOOGLE_GENERATIVE_AI_API_KEY and process.env.GEMINI_API_KEY when
resolving googleApiKey before calling normalizeDirect. Concretely, update the
Promise.all result handling around getSettingValue(SETTINGS_KEYS.googleApiKey)
and the assignment to cachedDirect so that googleApiKey falls back to
process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GEMINI_API_KEY ||
geminiApiKeyLegacy, then pass that resolved value into normalizeDirect (retain
parseJsonSetting and normalizeDirect usages).

In `@types.ts`:
- Line 588: The EmbeddingProvider type was narrowed to 'google' causing a
mismatch with route schemas that still accept 'openai' | 'voyage' | 'cohere';
revert or widen EmbeddingProvider so it includes 'openai' | 'voyage' | 'cohere'
(and 'google' if intended) to restore compatibility with
AIAgent.embedding_provider and the API routes (or alternatively update the route
schemas to match the new narrowed set); locate the type alias EmbeddingProvider
in types.ts and adjust its union to match the accepted values used in the
ai-agents route handlers and data contracts.

---

Outside diff comments:
In `@app/api/ai-agents/`[id]/chat/route.ts:
- Around line 46-48: The schema enum for embedding_provider in
app/api/ai-agents/route.ts and app/api/ai-agents/[id]/route.ts must match the
actual EmbeddingProvider type (lib/ai/embeddings.ts) and the chat logic: change
the allowed values from ['google','openai','voyage','cohere'] to only ['google']
and update the update-endpoint variant to the optional form (e.g., use the
z.enum(['google']).optional() or equivalent) so that validation matches the
EmbeddingProvider type and EMBEDDING_API_KEY_MAP lookups won't return undefined.

In `@app/api/ai-agents/`[id]/test/route.ts:
- Around line 23-25: EMBEDDING_API_KEY_MAP in
app/api/ai-agents/[id]/test/route.ts only contains a google entry so lookups for
agents whose embedding_provider is not "google" will fail; fix by (a) updating
the agent creation schema in app/api/ai-agents/route.ts to ensure
embedding_provider is validated/defaulted to supported providers (matching DB
values used elsewhere) and (b) making EMBEDDING_API_KEY_MAP and the lookup in
test/route.ts robust: either add entries for all supported providers or guard
the lookup (e.g., fall back to a safe default or throw a clear error) so
getEmbeddingApiKey(embedding_provider) cannot return undefined for valid DB
values. Ensure you update the symbols EMBEDDING_API_KEY_MAP and the agent
creation schema (where embedding_provider is defined) so both routes (chat and
test) handle non-google providers consistently.

In `@app/api/ai/models/route.ts`:
- Around line 77-95: The GET handler currently reads the tenant Google key and
calls fetchGoogleModels without authenticating; before calling getSettingValue
or fetchGoogleModels, call and await verifyApiKey() (importing it from
lib/auth.ts) and return an appropriate unauthorized response if it throws or
returns a falsy value. Update the GET function to first run verifyApiKey(), then
proceed to call getSettingValue('google_api_key') and fetchGoogleModels(apiKey)
only after successful verification, and ensure error handling still returns
NextResponse.json with status 502 for fetch errors and a 401/403 NextResponse
for auth failures.

In `@app/api/settings/ai/route.ts`:
- Around line 58-119: This route exposes AI settings without auth; import and
call verifyApiKey() from lib/auth.ts at the start of each handler (GET, POST,
DELETE) in this file and return an unauthorized NextResponse if verification
fails; ensure the call is awaited (e.g., const ok = await verifyApiKey(req) or
wrap in try/catch if verifyApiKey throws) so no settings logic
(prepareAiDirectUpdate, prepareAiRoutesUpdate, etc.) runs for unauthenticated
requests.
- Around line 126-196: The POST handler accepts arbitrary JSON for routes,
prompts, and ocr_gemini_model and merges it into persisted settings which can
corrupt downstream readers; add explicit Zod validation in the POST function to
parse and validate the incoming body (provider, model, google_api_key, routes,
prompts, ocr_gemini_model) before any merges, rejecting invalid payloads with a
400; for routes and prompts validate against the existing normalized shapes used
by prepareAiRoutesUpdate and the prompts normalization
(utilityGenerationTemplate, utilityJudgeTemplate, flowFormTemplate and optional
strategyMarketing/strategyUtility/strategyBypass) and only write validated
fields to updates, and validate ocr_gemini_model against an allowed string/enum
schema before pushing key 'ocr_gemini_model' to updates.

In `@lib/ai/ai-center-defaults.ts`:
- Around line 14-29: Remove the stale openaiApiKey handling: delete the
destructuring that pulls openaiApiKey and any derived boolean like hasOpenAIKey
in the module (e.g., the openaiApiKey variable and hasOpenAIKey check in
system.ts), and remove any conditional branches that only run when hasOpenAIKey
is true; ensure all remaining logic uses the current AiDirectConfig fields (like
googleApiKey) and that no other references to openaiApiKey or hasOpenAIKey
remain.

In `@lib/mcp/tools/settings.ts`:
- Around line 81-103: The tool sz.settings.set_ai currently sends the old route
keys (inbox_suggest, inbox_chat, template_generation, ocr) which the backend
silently drops; fix this by normalizing the routes before sending—import and
call prepareAiRoutesUpdate(routes) (or otherwise map the incoming routes to the
backend's expected keys like generateUtilityTemplates/generateFlowForm) and
include the normalized result in the POST body instead of the raw routes
variable; update the async handler in settings.ts to use the output of
prepareAiRoutesUpdate when building JSON.stringify({ model, google_api_key,
routes: normalizedRoutes }).

---

Nitpick comments:
In `@app/`(dashboard)/settings/ai/page.tsx:
- Around line 481-502: The destructuring of useSettingsAIController includes
unused values models, modelsLoading, and fetchModels; remove these three
identifiers from the destructuring assignment in page.tsx and also remove them
from the return object of useSettingsAIController (or cease returning them) so
the hook signature and its consumers stay consistent (refer to
useSettingsAIController and the destructured list including
isDevMode/provider/model/.../handleStrategiesToggle).

In `@app/api/ai/models/route.ts`:
- Around line 87-93: The catch block in route.ts currently returns only { error
} losing the provider error text; update the error response inside the catch
(where message is derived and NextResponse.json is returned) to include details:
message (i.e., return { error: `Falha ao buscar modelos: ${message}`, details:
message } with the existing status 502) so clients following the repository-wide
API convention receive the provider failure text.

In `@components/features/settings/AIGatewayPanel.tsx`:
- Around line 60-183: This component duplicates API/client logic (loadConfig,
fetchModels, handleSaveKey, handleRemoveKey, handleSelectModel) that already
lives in hooks/useSettingsAI.ts; refactor AIGatewayPanel to be a pure
presentational component that consumes the hook instead of re-implementing it:
remove the internal loadConfig, fetchModels, handleSaveKey, handleRemoveKey,
handleSelectModel and providerState management, import and call useSettingsAI to
get state (providerState, activeModel, loading, saving) and handlers
(loadConfig/fetchModels/saveKey/removeKey/selectModel or similarly named
functions exported by the hook), wire those hook values/handlers into the JSX
props/controls, and ensure any local UI-only state (e.g., showKey, modelSearch)
remains minimal or is lifted into typed props; keep component file purely
presentational with typed props if needed and rely on unique symbols loadConfig,
fetchModels, handleSaveKey, handleRemoveKey, handleSelectModel and the hook
useSettingsAI for the remote logic.

In `@lib/ai/ai-center-defaults.ts`:
- Line 25: Update the stale JSDoc comment that currently reads "Model ID no
formato bare, sem prefixo de provider (ex: 'gemini-2.5-flash', 'gpt-5.4')" to
remove the OpenAI example and only show valid Gemini examples; locate the
comment in lib/ai/ai-center-defaults.ts (the "Model ID no formato bare..." JSDoc
above the model ID declaration) and replace the example list so it only includes
Gemini model IDs (e.g., 'gemini-2.5-flash') and any other supported provider
examples if applicable.

In `@lib/ai/services/ai-judge.ts`:
- Around line 30-32: The Google-only path in ai-judge.ts ignores the
caller-supplied key in JudgeOptions, so update the Google client creation to
prefer options.apiKey over the DB setting (use options.apiKey ??
config.googleApiKey when calling createGoogleGenerativeAI) and pass that value
into google(targetModelId); alternatively remove apiKey from JudgeOptions if
intentional. Locate the code around createGoogleGenerativeAI, the google
variable and the model assignment (google(targetModelId)) and apply the key
preference or remove the option from the public contract accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 495d4853-9037-4529-a86b-063dc5833b2e

📥 Commits

Reviewing files that changed from the base of the PR and between 17c69e2 and b6e039c.

📒 Files selected for processing (27)
  • app/(dashboard)/settings/ai/page.tsx
  • app/api/ai-agents/[id]/chat/route.ts
  • app/api/ai-agents/[id]/test/route.ts
  • app/api/ai-agents/embedding-providers/route.ts
  • app/api/ai-agents/knowledge/route.ts
  • app/api/ai-agents/llm-providers/route.ts
  • app/api/ai/models/route.ts
  • app/api/inbox/chat/route.ts
  • app/api/inbox/suggest/route.ts
  • app/api/settings/ai/route.ts
  • app/api/settings/all/route.ts
  • components/features/settings/AIGatewayPanel.tsx
  • components/features/settings/ai-agents/AIAgentForm.tsx
  • components/features/settings/types.ts
  • hooks/useSettingsAI.ts
  • lib/ai/agents/chat-agent.ts
  • lib/ai/ai-center-config.ts
  • lib/ai/ai-center-defaults.ts
  • lib/ai/embeddings.ts
  • lib/ai/providers.ts
  • lib/ai/services/ai-judge.ts
  • lib/ai/services/template-agent.ts
  • lib/ai/unified-ai-service.ts
  • lib/mcp/tools/settings.ts
  • lib/supabase-db.ts
  • services/settingsService.ts
  • types.ts
💤 Files with no reviewable changes (3)
  • app/api/ai-agents/knowledge/route.ts
  • app/api/ai-agents/llm-providers/route.ts
  • app/api/ai-agents/embedding-providers/route.ts

Comment on lines +132 to +139
const [rawDirect, googleApiKey, geminiApiKeyLegacy] = await Promise.all([
getSettingValue(SETTINGS_KEYS.direct),
getSettingValue(SETTINGS_KEYS.googleApiKey),
getSettingValue('gemini_api_key'), // retrocompatibilidade: chave pode estar salva com nome antigo
getSettingValue(SETTINGS_KEYS.openaiApiKey),
])

const parsed = parseJsonSetting<Partial<Pick<AiDirectConfig, 'provider' | 'model'>>>(rawDirect, {})
cachedDirect = normalizeDirect(parsed, googleApiKey || geminiApiKeyLegacy, openaiApiKey)
cachedDirect = normalizeDirect(parsed, googleApiKey || geminiApiKeyLegacy)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore env-key fallback in getAiDirectConfig().

This path now only checks Supabase plus the legacy gemini_api_key row. Deployments relying on GEMINI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY will suddenly look unconfigured, and downstream callers like lib/ai/agents/chat-agent.ts will fail with the new “Chave Google não configurada” path.

Suggested fix
 export async function getAiDirectConfig(): Promise<AiDirectConfig> {
   if (cachedDirect && isCacheValid()) return cachedDirect
 
   const [rawDirect, googleApiKey, geminiApiKeyLegacy] = await Promise.all([
     getSettingValue(SETTINGS_KEYS.direct),
     getSettingValue(SETTINGS_KEYS.googleApiKey),
     getSettingValue('gemini_api_key'), // retrocompatibilidade: chave pode estar salva com nome antigo
   ])
+  const envGoogleApiKey =
+    process.env.GEMINI_API_KEY ||
+    process.env.GOOGLE_GENERATIVE_AI_API_KEY ||
+    null
 
   const parsed = parseJsonSetting<Partial<Pick<AiDirectConfig, 'provider' | 'model'>>>(rawDirect, {})
-  cachedDirect = normalizeDirect(parsed, googleApiKey || geminiApiKeyLegacy)
+  cachedDirect = normalizeDirect(
+    parsed,
+    googleApiKey || geminiApiKeyLegacy || envGoogleApiKey
+  )
   cacheTime = Date.now()
   return cachedDirect
 }

Based on learnings: Support environment variable aliases: GEMINI_API_KEY/GOOGLE_GENERATIVE_AI_API_KEY.

📝 Committable suggestion

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

Suggested change
const [rawDirect, googleApiKey, geminiApiKeyLegacy] = await Promise.all([
getSettingValue(SETTINGS_KEYS.direct),
getSettingValue(SETTINGS_KEYS.googleApiKey),
getSettingValue('gemini_api_key'), // retrocompatibilidade: chave pode estar salva com nome antigo
getSettingValue(SETTINGS_KEYS.openaiApiKey),
])
const parsed = parseJsonSetting<Partial<Pick<AiDirectConfig, 'provider' | 'model'>>>(rawDirect, {})
cachedDirect = normalizeDirect(parsed, googleApiKey || geminiApiKeyLegacy, openaiApiKey)
cachedDirect = normalizeDirect(parsed, googleApiKey || geminiApiKeyLegacy)
const [rawDirect, googleApiKey, geminiApiKeyLegacy] = await Promise.all([
getSettingValue(SETTINGS_KEYS.direct),
getSettingValue(SETTINGS_KEYS.googleApiKey),
getSettingValue('gemini_api_key'), // retrocompatibilidade: chave pode estar salva com nome antigo
])
const envGoogleApiKey =
process.env.GEMINI_API_KEY ||
process.env.GOOGLE_GENERATIVE_AI_API_KEY ||
null
const parsed = parseJsonSetting<Partial<Pick<AiDirectConfig, 'provider' | 'model'>>>(rawDirect, {})
cachedDirect = normalizeDirect(
parsed,
googleApiKey || geminiApiKeyLegacy || envGoogleApiKey
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/ai/ai-center-config.ts` around lines 132 - 139, The getAiDirectConfig()
path currently reads only Supabase keys and a legacy 'gemini_api_key' row;
restore the environment-variable fallback by checking
process.env.GOOGLE_GENERATIVE_AI_API_KEY and process.env.GEMINI_API_KEY when
resolving googleApiKey before calling normalizeDirect. Concretely, update the
Promise.all result handling around getSettingValue(SETTINGS_KEYS.googleApiKey)
and the assignment to cachedDirect so that googleApiKey falls back to
process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GEMINI_API_KEY ||
geminiApiKeyLegacy, then pass that resolved value into normalizeDirect (retain
parseJsonSetting and normalizeDirect usages).

Comment thread types.ts

// T004: AIAgent interface
export type EmbeddingProvider = 'google' | 'openai' | 'voyage' | 'cohere';
export type EmbeddingProvider = 'google';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep this type aligned with the route schemas until those endpoints are narrowed too.

app/api/ai-agents/route.ts and app/api/ai-agents/[id]/route.ts still accept 'openai' | 'voyage' | 'cohere' for embedding_provider, so narrowing EmbeddingProvider to 'google' makes AIAgent.embedding_provider incompatible with the current API/data contract. Loading or editing legacy agents will now require casts or fail type checks.

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

In `@types.ts` at line 588, The EmbeddingProvider type was narrowed to 'google'
causing a mismatch with route schemas that still accept 'openai' | 'voyage' |
'cohere'; revert or widen EmbeddingProvider so it includes 'openai' | 'voyage' |
'cohere' (and 'google' if intended) to restore compatibility with
AIAgent.embedding_provider and the API routes (or alternatively update the route
schemas to match the new narrowed set); locate the type alias EmbeddingProvider
in types.ts and adjust its union to match the accepted values used in the
ai-agents route handlers and data contracts.

@thaleslaray

Copy link
Copy Markdown
Owner Author

Code review

Found 1 issue:

  1. Runtime crash when agent has a non-Google embedding_provider stored in the DBEMBEDDING_API_KEY_MAP was narrowed to { google: ... } only, but chat/route.ts and test/route.ts look up the map with no fallback. If agent.embedding_provider is 'openai', 'voyage', or 'cohere' (possible for agents created before this PR), config is undefined and config.settingKey throws a TypeError. Notably, knowledge/route.ts already has the correct pattern: EMBEDDING_API_KEY_MAP[provider] || EMBEDDING_API_KEY_MAP.google.

    Additionally, app/api/ai-agents/route.ts:32 still validates embedding_provider as z.enum(['google', 'openai', 'voyage', 'cohere']), so new agents can be created with non-Google providers and hit the same crash.

if (hasKnowledgeBase) {
const embeddingProvider = (agent.embedding_provider || 'google') as EmbeddingProvider
const config = EMBEDDING_API_KEY_MAP[embeddingProvider]
const { data: embeddingKeySetting } = await supabase
.from('settings')
.select('value')
.eq('key', config.settingKey)
.maybeSingle()

// Get embedding API key for the configured provider
const embeddingProvider = (agent.embedding_provider || 'google') as EmbeddingProvider
const config = EMBEDDING_API_KEY_MAP[embeddingProvider]
const { data: embeddingKeySetting } = await supabase

// RAG: Embedding config
embedding_provider: z.enum(['google', 'openai', 'voyage', 'cohere']).default('google'),
embedding_model: z.string().default('gemini-embedding-001'),

Fix: add || EMBEDDING_API_KEY_MAP.google fallback in both chat/route.ts and test/route.ts, and narrow the Zod enum in app/api/ai-agents/route.ts to z.enum(['google']).

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant