feat(llm): native support for OpenAI, Anthropic, and Gemini#45
feat(llm): native support for OpenAI, Anthropic, and Gemini#45saksenaaishwarya wants to merge 1 commit into
Conversation
Introduces an LLMProvider contract and factory under src/llm so the CLI can target Ollama, OpenAI, Anthropic, or Gemini based on a new `backend` config field. API keys resolve from LLM_API_KEY first, then the provider-specific variable (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY). When the selected backend is unavailable, behavior matches today's Ollama-down path and falls back to the rule-based engine. Existing public exports in src/llm/checkOllama and src/llm/ollamaEnhancer are preserved as thin re-exports so the gitbun-vscode extension and any external consumers keep compiling. Closes nirvik34#34
|
@saksenaaishwarya is attempting to deploy a commit to the nirvik34's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning
|
| Layer / File(s) | Summary |
|---|---|
Type system and utilities src/llm/types.ts |
Introduces ProviderName, LLMProvider interface, ProviderConfig, and ProviderEnv types. Implements resolveApiKey() to handle generic LLM_API_KEY precedence over provider-specific keys. Exports COMMIT_SYSTEM_PROMPT, buildUserPrompt(), and cleanCommitOutput() helpers used by all providers. |
Provider factory pattern src/llm/index.ts, src/llm/factory.test.ts |
createProvider() factory dispatches to backend-specific provider creators (openai, anthropic, gemini, ollama) with exhaustiveness checking. Tests validate backend selection, API key gating for cloud providers, and isAvailable()/enhanceCommit() behavior. |
Cloud provider implementations src/llm/providers/openai.ts, src/llm/providers/anthropic.ts, src/llm/providers/gemini.ts, src/llm/providers/*.test.ts |
Each provider implements isAvailable(), resolveModel(), and enhanceCommit() using a 20s timeout, system/user prompts, and graceful fallback to original message on any error. Tests cover API key precedence, request formation, response parsing, and error paths including missing content and HTTP failures. |
Ollama provider refactor src/llm/providers/ollama.ts, src/llm/checkOllama.ts, src/llm/ollamaEnhancer.ts |
Ollama logic extracted into a provider module with functions for availability, model discovery (preferred-name matching with fallback), and commit enhancement. checkOllama.ts now re-exports from the provider module; ollamaEnhancer.ts delegates to enhanceCommitOllama(). |
Main entrypoint integration src/index.ts, README.md |
AI enhancement block replaced with provider-based flow: creates provider via factory, checks availability, resolves model from config/options, calls provider.enhanceCommit(), and falls back to rule-based engine on unavailability or exceptions. Documentation added describing all supported backends, environment variable setup, and fallback behavior. |
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested labels
level: advanced, quality:clean, type:testing
Suggested reviewers
- 2PieRadian
Poem
🐰 Four backends now dance in harmony,
From OpenAI to Gemini with glee,
A factory dispatches with type-safe care,
Fallbacks ensure robustness everywhere,
Ollama still leads, but clouds have their share! ☁️✨
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and concisely summarizes the main change: adding native support for three cloud LLM providers. |
| Linked Issues check | ✅ Passed | All coding objectives from issue #34 are met: modular provider interface, backend selection via config, API key resolution, soft-failure behavior, and backward compatibility with Ollama. |
| Out of Scope Changes check | ✅ Passed | All changes are directly aligned with issue #34's objectives; no unrelated modifications outside the provider abstraction, config, or documentation scope. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
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 @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/llm/providers/gemini.ts`:
- Around line 52-55: The request currently embeds apiKey into the request URL
(constructed via GEMINI_BASE_URL, model, and apiKey) and later logs the raw
error object on failures, risking credential leakage; change the request to
remove apiKey from the URL (keep GEMINI_BASE_URL and model encoding) and send
the key in a header (e.g., Authorization: Bearer <apiKey> or an appropriate
X-API-Key header) instead, and replace the raw error dump with a sanitized log
that includes only non-sensitive details (error.message, status code or a
generic failure message) and ensure any logged strings do not contain apiKey or
its encoded form.
In `@src/llm/providers/ollama.ts`:
- Around line 133-135: In resolveModel, the function currently returns the
user-provided requested value as-is which allows whitespace-only strings to
bypass model discovery; change the logic in resolveModel to trim requested
(e.g., const r = requested?.trim()) and only return it when r is a non-empty
string, otherwise call getBestModel(env) and return that (or null) so
whitespace-only input doesn't skip auto-selection.
In `@src/llm/types.ts`:
- Around line 61-65: cleanCommitOutput currently only strips a single wrapper
char and splits on "\n", which leaves triple-backtick fences and CRLF sequences
intact and also the function does not return the final value; update
cleanCommitOutput to first normalize line endings (convert CRLF to LF), then
trim whitespace, strip any surrounding fence or quote sequences (handle
one-or-more leading/trailing backticks or quotes and optional fenced-language
markers like ```js), remove a leading case-insensitive "commit:" token, then
take the first line (split on LF) and return that string; reference the
cleanCommitOutput function to locate and implement these changes.
🪄 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: ca047072-a538-4712-91f8-33338404b743
📒 Files selected for processing (14)
README.mdsrc/index.tssrc/llm/checkOllama.tssrc/llm/factory.test.tssrc/llm/index.tssrc/llm/ollamaEnhancer.tssrc/llm/providers/anthropic.test.tssrc/llm/providers/anthropic.tssrc/llm/providers/gemini.test.tssrc/llm/providers/gemini.tssrc/llm/providers/ollama.tssrc/llm/providers/openai.test.tssrc/llm/providers/openai.tssrc/llm/types.ts
| const url = `${GEMINI_BASE_URL}/${encodeURIComponent( | ||
| model | ||
| )}:generateContent?key=${encodeURIComponent(apiKey)}`; | ||
|
|
There was a problem hiding this comment.
Avoid potential API-key leakage in error logs.
Line 52-Line 55 places the key in the URL, and Line 87 logs the raw error object. On request failures, this can leak credentials into logs. Log a sanitized message instead of dumping the full error object.
🔒 Proposed fix
- } catch (error) {
- console.log("\nAI Enhancement Failed:", error);
+ } catch (error) {
+ const reason = error instanceof Error ? error.name : "unknown";
+ console.log(`\nGemini request failed (${reason}).`);
return originalMessage;
} finally {Also applies to: 86-87
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/llm/providers/gemini.ts` around lines 52 - 55, The request currently
embeds apiKey into the request URL (constructed via GEMINI_BASE_URL, model, and
apiKey) and later logs the raw error object on failures, risking credential
leakage; change the request to remove apiKey from the URL (keep GEMINI_BASE_URL
and model encoding) and send the key in a header (e.g., Authorization: Bearer
<apiKey> or an appropriate X-API-Key header) instead, and replace the raw error
dump with a sanitized log that includes only non-sensitive details
(error.message, status code or a generic failure message) and ensure any logged
strings do not contain apiKey or its encoded form.
| async resolveModel(requested?: string): Promise<string | null> { | ||
| if (requested) return requested; | ||
| const best = await getBestModel(env); |
There was a problem hiding this comment.
Trim user-provided model before bypassing auto-selection.
Line 134 returns requested as-is. A whitespace-only value will skip discovery and send an invalid model downstream.
✅ Proposed fix
async resolveModel(requested?: string): Promise<string | null> {
- if (requested) return requested;
+ const trimmed = requested?.trim();
+ if (trimmed) return trimmed;
const best = await getBestModel(env);
return best ?? FALLBACK_MODEL;
},📝 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.
| async resolveModel(requested?: string): Promise<string | null> { | |
| if (requested) return requested; | |
| const best = await getBestModel(env); | |
| async resolveModel(requested?: string): Promise<string | null> { | |
| const trimmed = requested?.trim(); | |
| if (trimmed) return trimmed; | |
| const best = await getBestModel(env); | |
| return best ?? FALLBACK_MODEL; | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/llm/providers/ollama.ts` around lines 133 - 135, In resolveModel, the
function currently returns the user-provided requested value as-is which allows
whitespace-only strings to bypass model discovery; change the logic in
resolveModel to trim requested (e.g., const r = requested?.trim()) and only
return it when r is a non-empty string, otherwise call getBestModel(env) and
return that (or null) so whitespace-only input doesn't skip auto-selection.
| export function cleanCommitOutput(raw: string): string { | ||
| let result = raw.trim(); | ||
| result = result.replace(/^[`"']|[`"']$/g, ""); | ||
| result = result.replace(/^commit:\s*/i, ""); | ||
| result = result.split("\n")[0]; |
There was a problem hiding this comment.
Harden output cleaning for fenced/CRLF responses.
Current stripping removes only a single wrapper character and can leave malformed output for responses like triple backticks or CRLF lines.
Suggested patch
export function cleanCommitOutput(raw: string): string {
let result = raw.trim();
- result = result.replace(/^[`"']|[`"']$/g, "");
+ result = result.replace(/^[`"']+|[`"']+$/g, "");
result = result.replace(/^commit:\s*/i, "");
- result = result.split("\n")[0];
- return result;
+ result = result.split(/\r?\n/)[0]?.trim() ?? "";
+ return result;
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/llm/types.ts` around lines 61 - 65, cleanCommitOutput currently only
strips a single wrapper char and splits on "\n", which leaves triple-backtick
fences and CRLF sequences intact and also the function does not return the final
value; update cleanCommitOutput to first normalize line endings (convert CRLF to
LF), then trim whitespace, strip any surrounding fence or quote sequences
(handle one-or-more leading/trailing backticks or quotes and optional
fenced-language markers like ```js), remove a leading case-insensitive "commit:"
token, then take the first line (split on LF) and return that string; reference
the cleanCommitOutput function to locate and implement these changes.
nirvik34
left a comment
There was a problem hiding this comment.
Resolve the merge conflict
seciruty issue of gemini api... Move the API key to a header (e.g., Authorization: Bearer or X-API-Key) and remove it from the URL
In ollama.ts trim the requested value and only return it if non-empty; otherwise call getBestModel().
Summary
Closes #34. Adds a modular
LLMProviderinterface undersrc/llm/so Gitbun can talk to OpenAI, Anthropic, and Gemini in addition to Ollama. Backend is picked via the existing config layer (.smartcommitrc/package.json#smartcommit); API keys come from the environment.Root cause / motivation
src/llm/was hardcoded to a local Ollama HTTP API. Contributors and CI users without a GPU asked for hosted-model support but the call sites insrc/index.tsimported Ollama-specific functions directly, so adding a backend would have required if/else trees in every call path.Solution
LLMProvidercontract insrc/llm/types.tswithisAvailable(),resolveModel(),enhanceCommit().src/llm/providers/(ollama.ts,openai.ts,anthropic.ts,gemini.ts), each using the existingnode-fetchdependency against the vendor's REST API — no SDK adds.createProvider(config, env)factory insrc/llm/index.tsselects the backend; default staysollamafor backwards compatibility.src/index.tsswapped to the factory; spinner / fallback messages are now provider-aware ("openai is not available. Using rule-based commit.").src/llm/checkOllama.tsandsrc/llm/ollamaEnhancer.tsreduced to thin re-exports so thegitbun-vscodeextension and any external imports keep compiling.Key changes
backendconfig field:ollama(default) |openai|anthropic|gemini.LLM_API_KEY→{PROVIDER}_API_KEY.gpt-4o-mini,claude-haiku-4-5-20251001,gemini-1.5-flash. Overridable with the existing--model <name>flag ormodelconfig key.Testing
npm run lintclean;tsc --noEmitclean.Backwards compatibility / risks
ollama; existing.smartcommitrcfiles behave identically.OLLAMA_HOSTenv var still honored.enhanceCommit,isOllamaRunning,getBestModel,getAvailableModels) kept at their original paths — the VSCode extension is unaffected.Checklist
Summary by CodeRabbit
New Features
LLM_API_KEYpreferredDocumentation