Skip to content

Support provider-specific API key configuration#31

Merged
agsaru merged 4 commits into
agsaru:mainfrom
RajanyaSaha-27:provider-abstraction
Jul 11, 2026
Merged

Support provider-specific API key configuration#31
agsaru merged 4 commits into
agsaru:mainfrom
RajanyaSaha-27:provider-abstraction

Conversation

@RajanyaSaha-27

@RajanyaSaha-27 RajanyaSaha-27 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR improves provider-specific API key handling.(fixes #16 )

Changes

  • Added get_api_key(provider) support.
  • Prompt only for the selected provider's API key.
  • Preserved existing get_api_keys() behavior for backward compatibility.
  • Updated CLI to configure environment variables based on the selected provider.
  • Kept existing tests passing.

Testing

  • Ran python -m pytest
  • All tests passed (16/16)
  • Verified repo2readme run --help
  • Tested provider-specific API key prompt

Summary by CodeRabbit

  • New Features

    • Added CLI options to choose an AI provider, model, and base URL for both code summaries and README generation/review.
    • README documentation updated to reflect the new llm/ folder.
  • Bug Fixes

    • Improved API key handling by prompting for missing credentials per provider and saving them for future runs.
    • Preserved the previous default behavior when no provider/model/base URL is specified.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee32b061-2c12-427e-83aa-cf6b618d0319

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc48cd and 3c3f9eb.

📒 Files selected for processing (2)
  • pyproject.toml
  • repo2readme/llm/factory.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • repo2readme/llm/factory.py

📝 Walkthrough

Walkthrough

Introduces a provider-agnostic LLM factory supporting Groq, Google/Gemini, OpenAI, Anthropic, and OpenRouter. Adds provider-specific API-key handling and threads provider, model, and base URL options through the CLI, workflow, summarization, README generation, and review pipeline.

Changes

Configurable LLM Provider Support

Layer / File(s) Summary
LLM factory and provider API-key configuration
repo2readme/llm/factory.py, repo2readme/config.py, pyproject.toml
Adds provider-based LangChain model construction, per-provider API-key lookup and prompting, and the OpenAI and Anthropic integrations.
CLI options and workflow state wiring
repo2readme/cli/main.py, repo2readme/readme/agent_workflow.py
Adds --provider, --model, and --base-url, conditionally configures provider keys, forwards options to summarization, and stores them in workflow state.
Summarization, README generation, and review integration
repo2readme/summarize/summary.py, repo2readme/readme/readme_generator.py, repo2readme/readme/reviewer_agent.py
Routes model creation through create_llm while forwarding provider, model, and base URL values and retaining existing prompt and parsing behavior.
README folder structure update
README.md
Adds llm/factory.py to the documented folder structure.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as run command
  participant Config as get_api_key
  participant Summary as summarize_file
  participant Workflow as ReadmeState
  participant Factory as create_llm

  CLI->>Config: get_api_key(provider)
  Config-->>CLI: provider API key
  CLI->>Summary: provider, model, base_url
  Summary->>Factory: create_llm(provider, model_name, base_url)
  Factory-->>Summary: BaseChatModel
  CLI->>Workflow: initial_state(provider, model, base_url)
  Workflow->>Factory: create_llm through generation and review nodes
  Factory-->>Workflow: BaseChatModel
Loading

Possibly related PRs

  • agsaru/Repo2Readme#19: Both changes modify the CLI run flow around API-key setup and summarize_file invocation.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [#16] The core provider/model refactor is in place, but the README configuration examples and test coverage requested by the issue are not shown in the diff. Add README configuration examples and tests covering dynamic provider selection, and verify the new provider options work for all supported paths.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: provider-specific API key configuration.
Out of Scope Changes check ✅ Passed The changes stay within scope: provider abstraction, config refactor, CLI wiring, and dependency additions all support #16.
✨ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
repo2readme/cli/main.py (1)

146-156: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate the provider-to-env-var mapping.

provider_env is an exact copy of provider_map from config.py's get_api_key. Maintaining two independent copies risks silent drift — as already seen with "together" being present here but missing in create_llm. Export the mapping from config.py and reuse it.

♻️ Proposed refactor: export and reuse provider mapping

In config.py, move the mapping to module level and export it:

+PROVIDER_ENV_VARS = {
+    "groq": "GROQ_API_KEY",
+    "google": "GOOGLE_API_KEY",
+    "gemini": "GOOGLE_API_KEY",
+    "openai": "OPENAI_API_KEY",
+    "anthropic": "ANTHROPIC_API_KEY",
+    "openrouter": "OPENROUTER_API_KEY",
+    "together": "TOGETHER_API_KEY",
+}
+
 def get_api_key(provider: str):
     env = load_env()
-    provider_map = {
-        "groq": "GROQ_API_KEY",
-        "google": "GOOGLE_API_KEY",
-        "gemini": "GOOGLE_API_KEY",
-        "openai": "OPENAI_API_KEY",
-        "anthropic": "ANTHROPIC_API_KEY",
-        "openrouter": "OPENROUTER_API_KEY",
-        "together": "TOGETHER_API_KEY",
-    }
+    provider_map = PROVIDER_ENV_VARS

In main.py, import and reuse:

-from repo2readme.config import (
+from repo2readme.config import (
     get_api_keys,
     get_api_key,
     reset_api_keys,
+    PROVIDER_ENV_VARS,
 )
             if provider:
                 api_key = get_api_key(provider)
-                provider_env = {
-                    "groq": "GROQ_API_KEY",
-                    "google": "GOOGLE_API_KEY",
-                    "gemini": "GOOGLE_API_KEY",
-                    "openai": "OPENAI_API_KEY",
-                    "anthropic": "ANTHROPIC_API_KEY",
-                    "openrouter": "OPENROUTER_API_KEY",
-                    "together": "TOGETHER_API_KEY",
-            }
-                os.environ[provider_env[provider.lower()]] = api_key
+                os.environ[PROVIDER_ENV_VARS[provider.lower()]] = api_key
🤖 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 `@repo2readme/cli/main.py` around lines 146 - 156, The provider-to-env-var
mapping is duplicated in main.py and config.py, so reuse the single source of
truth instead of maintaining a local copy. Move the provider mapping to module
scope in config.py (alongside get_api_key) and export it, then update main.py to
import and use that shared mapping when setting os.environ in the API key flow.
Make sure any provider handling used by create_llm and get_api_key stays aligned
through the shared mapping.
🤖 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 `@repo2readme/config.py`:
- Around line 57-62: The API key prompt flow in the config helper currently
saves whatever `input(...).strip()` returns, including an empty string when the
user just presses Enter. Update the API key handling logic around the prompt and
`save_env` call to validate that the trimmed value is non-empty before
persisting or returning it, and if it is empty, re-prompt or fail with a clear
message instead of writing it to the env file. Use the existing `provider`,
`env_var`, and `save_env` flow to keep the fix localized.

In `@repo2readme/llm/factory.py`:
- Around line 66-77: The create_llm factory is missing support for the
"together" provider, which causes a ValueError even though config.py and main.py
already accept it. Update create_llm in factory.py to handle provider ==
"together" alongside the existing provider branches, using the same pattern as
the other ChatOpenAI initializations and wiring the appropriate Together API
key/base URL settings. Keep the unsupported-provider ValueError only for truly
unknown providers so the provider flow stays consistent across files.

---

Nitpick comments:
In `@repo2readme/cli/main.py`:
- Around line 146-156: The provider-to-env-var mapping is duplicated in main.py
and config.py, so reuse the single source of truth instead of maintaining a
local copy. Move the provider mapping to module scope in config.py (alongside
get_api_key) and export it, then update main.py to import and use that shared
mapping when setting os.environ in the API key flow. Make sure any provider
handling used by create_llm and get_api_key stays aligned through the shared
mapping.
🪄 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: b0483916-a4c3-40ac-a9d6-092dbd74bee8

📥 Commits

Reviewing files that changed from the base of the PR and between 9770808 and 7fc48cd.

📒 Files selected for processing (9)
  • README.md
  • repo2readme/cli/main.py
  • repo2readme/config.py
  • repo2readme/llm/__init__.py
  • repo2readme/llm/factory.py
  • repo2readme/readme/agent_workflow.py
  • repo2readme/readme/readme_generator.py
  • repo2readme/readme/reviewer_agent.py
  • repo2readme/summarize/summary.py

Comment thread repo2readme/config.py
Comment on lines +57 to 62
rprint(f"[yellow]{provider} API key is missing![/yellow]\n")

api_key = input(f"Enter your {provider} API key: ").strip()

env[env_var] = api_key
save_env(env)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Validate non-empty API key before persisting.

input(...).strip() returns an empty string if the user presses Enter. The empty key is saved to the env file and returned, causing confusing authentication errors downstream with no clear root cause.

🛡️ Proposed fix: reject empty input
     rprint(f"[yellow]{provider} API key is missing![/yellow]\n")

     api_key = input(f"Enter your {provider} API key: ").strip()

+    if not api_key:
+        rprint("[red]API key cannot be empty. Aborting.[/red]")
+        raise ValueError("API key cannot be empty")
+
     env[env_var] = api_key
     save_env(env)
📝 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
rprint(f"[yellow]{provider} API key is missing![/yellow]\n")
api_key = input(f"Enter your {provider} API key: ").strip()
env[env_var] = api_key
save_env(env)
rprint(f"[yellow]{provider} API key is missing![/yellow]\n")
api_key = input(f"Enter your {provider} API key: ").strip()
if not api_key:
rprint("[red]API key cannot be empty. Aborting.[/red]")
raise ValueError("API key cannot be empty")
env[env_var] = api_key
save_env(env)
🤖 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 `@repo2readme/config.py` around lines 57 - 62, The API key prompt flow in the
config helper currently saves whatever `input(...).strip()` returns, including
an empty string when the user just presses Enter. Update the API key handling
logic around the prompt and `save_env` call to validate that the trimmed value
is non-empty before persisting or returning it, and if it is empty, re-prompt or
fail with a clear message instead of writing it to the env file. Use the
existing `provider`, `env_var`, and `save_env` flow to keep the fix localized.

Comment on lines +66 to +77
elif provider == "openrouter":
return ChatOpenAI(
model=model,
api_key=api_key or os.getenv("OPENROUTER_API_KEY"),
base_url=base_url or "https://openrouter.ai/api/v1",
**kwargs,
)

else:
raise ValueError(
f"Unsupported provider '{provider}'."
) No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Missing "together" provider creates cross-file inconsistency.

config.py's get_api_key and main.py's provider_env both support "together", but create_llm does not. A user passing --provider together would successfully configure an API key, then hit a ValueError during summarization — all documents fail and a poor README is generated from error dicts.

🔧 Proposed fix: add "together" provider support
     elif provider == "openrouter":
         return ChatOpenAI(
             model=model,
             api_key=api_key or os.getenv("OPENROUTER_API_KEY"),
             base_url=base_url or "https://openrouter.ai/api/v1",
             **kwargs,
         )

+    elif provider == "together":
+        return ChatOpenAI(
+            model=model,
+            api_key=api_key or os.getenv("TOGETHER_API_KEY"),
+            base_url=base_url or "https://api.together.xyz/v1",
+            **kwargs,
+        )
+
     else:
         raise ValueError(
             f"Unsupported provider '{provider}'."
         )
📝 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
elif provider == "openrouter":
return ChatOpenAI(
model=model,
api_key=api_key or os.getenv("OPENROUTER_API_KEY"),
base_url=base_url or "https://openrouter.ai/api/v1",
**kwargs,
)
else:
raise ValueError(
f"Unsupported provider '{provider}'."
)
elif provider == "openrouter":
return ChatOpenAI(
model=model,
api_key=api_key or os.getenv("OPENROUTER_API_KEY"),
base_url=base_url or "https://openrouter.ai/api/v1",
**kwargs,
)
elif provider == "together":
return ChatOpenAI(
model=model,
api_key=api_key or os.getenv("TOGETHER_API_KEY"),
base_url=base_url or "https://api.together.xyz/v1",
**kwargs,
)
else:
raise ValueError(
f"Unsupported provider '{provider}'."
)
🤖 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 `@repo2readme/llm/factory.py` around lines 66 - 77, The create_llm factory is
missing support for the "together" provider, which causes a ValueError even
though config.py and main.py already accept it. Update create_llm in factory.py
to handle provider == "together" alongside the existing provider branches, using
the same pattern as the other ChatOpenAI initializations and wiring the
appropriate Together API key/base URL settings. Keep the unsupported-provider
ValueError only for truly unknown providers so the provider flow stays
consistent across files.

@agsaru

agsaru commented Jul 11, 2026

Copy link
Copy Markdown
Owner

@RajanyaSaha-27 can u solve the errors mentioned by coderabbit. And the tests are failing check those also.

@agsaru agsaru merged commit 7cd3542 into agsaru:main Jul 11, 2026
4 checks passed
@RajanyaSaha-27 RajanyaSaha-27 deleted the provider-abstraction branch July 11, 2026 13:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Support Custom LLM Providers & Configurable Models

2 participants