refactor: add OAuth support to provider registry system
- Add is_oauth and oauth_provider fields to ProviderSpec - Update _make_provider() to use registry for OAuth provider detection - Update get_provider() to support OAuth providers (no API key required) - Mark OpenAI Codex as OAuth-based provider in registry This improves the provider registry architecture to support OAuth-based authentication flows, making it extensible for future OAuth providers. Benefits: - OAuth providers are now registry-driven (not hardcoded) - Extensible design: new OAuth providers only need registry entry - Backward compatible: existing API key providers unaffected - Clean separation: OAuth logic centralized in registry
This commit is contained in:
parent
c1dc8d3f55
commit
08efe6ad3f
@ -173,20 +173,33 @@ This file stores important information that should persist across sessions.
|
||||
|
||||
|
||||
def _make_provider(config):
|
||||
"""Create LiteLLMProvider from config. Exits if no API key found."""
|
||||
"""Create provider from config. Exits if no credentials found."""
|
||||
from nanobot.providers.litellm_provider import LiteLLMProvider
|
||||
from nanobot.providers.openai_codex_provider import OpenAICodexProvider
|
||||
from oauth_cli_kit import get_token as get_codex_token
|
||||
from nanobot.providers.registry import PROVIDERS
|
||||
from oauth_cli_kit import get_token as get_oauth_token
|
||||
|
||||
p = config.get_provider()
|
||||
model = config.agents.defaults.model
|
||||
if model.startswith("openai-codex/"):
|
||||
model_lower = model.lower()
|
||||
|
||||
# Check for OAuth-based providers first (registry-driven)
|
||||
for spec in PROVIDERS:
|
||||
if spec.is_oauth and any(kw in model_lower for kw in spec.keywords):
|
||||
# OAuth provider matched
|
||||
try:
|
||||
_ = get_codex_token()
|
||||
_ = get_oauth_token(spec.oauth_provider or spec.name)
|
||||
except Exception:
|
||||
console.print("Please run: [cyan]nanobot login --provider openai-codex[/cyan]")
|
||||
console.print(f"Please run: [cyan]nanobot login --provider {spec.name}[/cyan]")
|
||||
raise typer.Exit(1)
|
||||
# Return appropriate OAuth provider class
|
||||
if spec.name == "openai_codex":
|
||||
return OpenAICodexProvider(default_model=model)
|
||||
# Future OAuth providers can be added here
|
||||
console.print(f"[red]Error: OAuth provider '{spec.name}' not fully implemented.[/red]")
|
||||
raise typer.Exit(1)
|
||||
|
||||
# Standard API key-based providers
|
||||
p = config.get_provider()
|
||||
if not (p and p.api_key) and not model.startswith("bedrock/"):
|
||||
console.print("[red]Error: No API key configured.[/red]")
|
||||
console.print("Set one in ~/.nanobot/config.json under providers section")
|
||||
|
||||
@ -132,15 +132,19 @@ class Config(BaseSettings):
|
||||
model_lower = (model or self.agents.defaults.model).lower()
|
||||
|
||||
# Match by keyword (order follows PROVIDERS registry)
|
||||
# Note: OAuth providers don't require api_key, so we check is_oauth flag
|
||||
for spec in PROVIDERS:
|
||||
p = getattr(self.providers, spec.name, None)
|
||||
if p and any(kw in model_lower for kw in spec.keywords) and p.api_key:
|
||||
if p and any(kw in model_lower for kw in spec.keywords):
|
||||
# OAuth providers don't need api_key
|
||||
if spec.is_oauth or p.api_key:
|
||||
return p
|
||||
|
||||
# Fallback: gateways first, then others (follows registry order)
|
||||
# OAuth providers are also valid fallbacks
|
||||
for spec in PROVIDERS:
|
||||
p = getattr(self.providers, spec.name, None)
|
||||
if p and p.api_key:
|
||||
if p and (spec.is_oauth or p.api_key):
|
||||
return p
|
||||
return None
|
||||
|
||||
|
||||
@ -51,6 +51,10 @@ class ProviderSpec:
|
||||
# per-model param overrides, e.g. (("kimi-k2.5", {"temperature": 1.0}),)
|
||||
model_overrides: tuple[tuple[str, dict[str, Any]], ...] = ()
|
||||
|
||||
# OAuth-based providers (e.g., OpenAI Codex) don't use API keys
|
||||
is_oauth: bool = False # if True, uses OAuth flow instead of API key
|
||||
oauth_provider: str = "" # OAuth provider name for token retrieval
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
return self.display_name or self.name.title()
|
||||
@ -157,6 +161,8 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
||||
default_api_base="https://chatgpt.com/backend-api",
|
||||
strip_model_prefix=False,
|
||||
model_overrides=(),
|
||||
is_oauth=True, # OAuth-based authentication
|
||||
oauth_provider="openai-codex", # OAuth provider identifier
|
||||
),
|
||||
|
||||
# DeepSeek: needs "deepseek/" prefix for LiteLLM routing.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user