Mirrowel commited on
Commit
e8d627c
·
1 Parent(s): 2e16e7e

feat(auth): add interactive API key setup supporting 70+ providers

Browse files

Refactors the credential setup utility to integrate support for standard API keys alongside existing OAuth providers.

Key changes:
- The main menu is updated to allow selection between OAuth credential setup and API key setup.
- Implements `setup_api_key` to interactively guide the user through selecting from a list of recognized LiteLLM providers.
- API keys are automatically saved to the project's `.env` file, utilizing key indexing (e.g., `OPENAI_API_KEY_1`) to support multiple keys per provider.
- Introduces `ensure_env_defaults` to automatically initialize the `.env` file and set essential default values like `PROXY_API_KEY`.

Files changed (2) hide show
  1. setup_env.bat +0 -121
  2. src/rotator_library/credential_tool.py +180 -23
setup_env.bat DELETED
@@ -1,121 +0,0 @@
1
- @echo off
2
- setlocal enabledelayedexpansion
3
-
4
- REM --- Configuration ---
5
- set "ENV_FILE=.env"
6
- set "DEFAULT_PROXY_KEY=VerysecretKey"
7
-
8
- REM --- Provider Name to Variable Name Mapping ---
9
- set "provider_count=0"
10
- set "provider_list[1]=Gemini" & set "provider_vars[1]=GEMINI" & set /a provider_count+=1
11
- set "provider_list[2]=OpenRouter" & set "provider_vars[2]=OPENROUTER" & set /a provider_count+=1
12
- set "provider_list[3]=Chutes" & set "provider_vars[3]=CHUTES" & set /a provider_count+=1
13
- set "provider_list[4]=Nvidia" & set "provider_vars[4]=NVIDIA_NIM" & set /a provider_count+=1
14
- set "provider_list[5]=OpenAI" & set "provider_vars[5]=OPENAI" & set /a provider_count+=1
15
- set "provider_list[6]=Anthropic" & set "provider_vars[6]=ANTHROPIC" & set /a provider_count+=1
16
- set "provider_list[7]=Mistral" & set "provider_vars[7]=MISTRAL" & set /a provider_count+=1
17
- set "provider_list[8]=Groq" & set "provider_vars[8]=GROQ" & set /a provider_count+=1
18
- set "provider_list[9]=Cohere" & set "provider_vars[9]=COHERE" & set /a provider_count+=1
19
- set "provider_list[10]=Bedrock" & set "provider_vars[10]=BEDROCK" & set /a provider_count+=1
20
-
21
-
22
- :main
23
- cls
24
- echo =================================================================
25
- echo Welcome to the API Key Setup for Your Proxy Server
26
- echo =================================================================
27
- echo.
28
- echo This script will help you set up your .env file.
29
- echo.
30
-
31
- REM --- Ensure .env file exists and has PROXY_API_KEY ---
32
- if not exist "%ENV_FILE%" (
33
- echo Creating a new %ENV_FILE% file for you...
34
- echo PROXY_API_KEY="%DEFAULT_PROXY_KEY%" > "%ENV_FILE%"
35
- echo.
36
- ) else (
37
- findstr /C:"PROXY_API_KEY=" "%ENV_FILE%" >nul
38
- if errorlevel 1 (
39
- echo Adding the default proxy key to your .env file...
40
- echo.>> "%ENV_FILE%"
41
- echo PROXY_API_KEY="%DEFAULT_PROXY_KEY%" >> "%ENV_FILE%"
42
- echo.
43
- )
44
- )
45
-
46
- :get_provider
47
- echo -----------------------------------------------------------------
48
- echo Please choose a provider to add an API key for:
49
- echo -----------------------------------------------------------------
50
- echo.
51
- for /L %%i in (1,1,%provider_count%) do (
52
- echo %%i. !provider_list[%%i]!
53
- )
54
- echo.
55
- set /p "choice=Type the number of the provider and press Enter: "
56
-
57
- REM --- Validate Provider Choice ---
58
- set "VAR_NAME="
59
- set "provider_choice="
60
- if %choice% GTR 0 if %choice% LEQ %provider_count% (
61
- set "VAR_NAME=!provider_vars[%choice%]!"
62
- set "provider_choice=!provider_list[%choice%]!"
63
- )
64
-
65
- if not defined VAR_NAME (
66
- cls
67
- echo =================================================================
68
- echo INVALID SELECTION! Please try again.
69
- echo =================================================================
70
- echo.
71
- pause
72
- goto :get_provider
73
- )
74
-
75
- set "API_VAR_BASE=%VAR_NAME%_API_KEY"
76
-
77
- :get_key
78
- echo.
79
- echo -----------------------------------------------------------------
80
- set /p "api_key=Enter the API key for %provider_choice%: "
81
- if not defined api_key (
82
- echo You must enter an API key.
83
- goto :get_key
84
- )
85
- echo -----------------------------------------------------------------
86
- echo.
87
-
88
- REM --- Find the next available key number ---
89
- set /a key_index=1
90
- :find_next_key
91
- findstr /R /C:"^%API_VAR_BASE%_%key_index% *=" "%ENV_FILE%" >nul
92
- if %errorlevel% equ 0 (
93
- set /a key_index+=1
94
- goto :find_next_key
95
- )
96
-
97
- REM --- Append the new key to the .env file ---
98
- echo Adding your key to %ENV_FILE%...
99
- echo %API_VAR_BASE%_%key_index%="%api_key%" >> "%ENV_FILE%"
100
- echo.
101
- echo Successfully added %provider_choice% API key as %API_VAR_BASE%_%key_index%!
102
- echo.
103
-
104
- :ask_another
105
- set /p "another=Do you want to add another key? (yes/no): "
106
- if /i "%another%"=="yes" (
107
- goto :main
108
- )
109
- if /i "%another%"=="y" (
110
- goto :main
111
- )
112
-
113
- cls
114
- echo =================================================================
115
- echo Setup Complete! Your .env file is ready.
116
- echo =================================================================
117
- echo.
118
- echo You can now run the proxy server.
119
- echo.
120
- pause
121
- exit /b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/rotator_library/credential_tool.py CHANGED
@@ -4,8 +4,10 @@ import asyncio
4
  import json
5
  import re
6
  from pathlib import Path
 
7
 
8
  from .provider_factory import get_provider_auth_class, get_available_providers
 
9
  from rich.console import Console
10
  from rich.panel import Panel
11
  from rich.prompt import Prompt
@@ -13,9 +15,138 @@ from rich.text import Text
13
 
14
  OAUTH_BASE_DIR = Path.cwd() / "oauth_creds"
15
  OAUTH_BASE_DIR.mkdir(exist_ok=True)
 
 
 
16
 
17
  console = Console()
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  async def setup_new_credential(provider_name: str):
20
  """
21
  Interactively sets up a new OAuth credential for a given provider.
@@ -66,39 +197,65 @@ async def setup_new_credential(provider_name: str):
66
 
67
  async def main():
68
  """
69
- An interactive CLI tool to add new OAuth credentials.
70
  """
 
71
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
72
 
73
  while True:
74
- available_providers = get_available_providers()
 
 
 
 
75
 
76
- provider_text = Text()
77
- for i, provider in enumerate(available_providers):
78
- provider_text.append(f" {i + 1}. {provider.capitalize()}\n")
79
-
80
- console.print(Panel(provider_text, title="Available Providers", style="bold blue"))
81
-
82
- choice = Prompt.ask(
83
- Text.from_markup("[bold]Please select a provider or type [red]'q'[/red] to quit[/bold]"),
84
- choices=[str(i + 1) for i in range(len(available_providers))] + ["q"],
85
  show_choices=False
86
  )
87
 
88
- if choice.lower() == 'q':
89
  break
90
 
91
- try:
92
- choice_index = int(choice) - 1
93
- if 0 <= choice_index < len(available_providers):
94
- provider_name = available_providers[choice_index]
95
- console.print(f"\nStarting setup for [bold cyan]{provider_name.capitalize()}[/bold cyan]...")
96
- await setup_new_credential(provider_name)
97
- else:
98
- console.print("[bold red]Invalid choice. Please try again.[/bold red]")
99
- except ValueError:
100
- console.print("[bold red]Invalid input. Please enter a number or 'q'.[/bold red]")
101
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  console.print("\n" + "="*50 + "\n")
103
 
104
  def run_credential_tool():
 
4
  import json
5
  import re
6
  from pathlib import Path
7
+ from dotenv import set_key, get_key
8
 
9
  from .provider_factory import get_provider_auth_class, get_available_providers
10
+ from .providers import PROVIDER_PLUGINS
11
  from rich.console import Console
12
  from rich.panel import Panel
13
  from rich.prompt import Prompt
 
15
 
16
  OAUTH_BASE_DIR = Path.cwd() / "oauth_creds"
17
  OAUTH_BASE_DIR.mkdir(exist_ok=True)
18
+ # Use a direct path to the .env file in the project root
19
+ ENV_FILE = Path.cwd() / ".env"
20
+
21
 
22
  console = Console()
23
 
24
+ def ensure_env_defaults():
25
+ """
26
+ Ensures the .env file exists and contains essential default values like PROXY_API_KEY.
27
+ """
28
+ if not ENV_FILE.is_file():
29
+ ENV_FILE.touch()
30
+ console.print(f"Creating a new [bold yellow]{ENV_FILE.name}[/bold yellow] file...")
31
+
32
+ # Check for PROXY_API_KEY, similar to setup_env.bat
33
+ if get_key(str(ENV_FILE), "PROXY_API_KEY") is None:
34
+ default_key = "VerysecretKey"
35
+ console.print(f"Adding default [bold cyan]PROXY_API_KEY[/bold cyan] to [bold yellow]{ENV_FILE.name}[/bold yellow]...")
36
+ set_key(str(ENV_FILE), "PROXY_API_KEY", default_key)
37
+
38
+ async def setup_api_key():
39
+ """
40
+ Interactively sets up a new API key for a provider.
41
+ """
42
+ console.print(Panel("[bold cyan]API Key Setup[/bold cyan]", expand=False))
43
+
44
+ # Verified list of LiteLLM providers with their friendly names and API key variables
45
+ LITELLM_PROVIDERS = {
46
+ "OpenAI": "OPENAI_API_KEY", "Anthropic": "ANTHROPIC_API_KEY",
47
+ "Google AI Studio (Gemini)": "GEMINI_API_KEY", "Azure OpenAI": "AZURE_API_KEY",
48
+ "Vertex AI": "GOOGLE_API_KEY", "AWS Bedrock": "AWS_ACCESS_KEY_ID",
49
+ "Cohere": "COHERE_API_KEY", "Mistral AI": "MISTRAL_API_KEY",
50
+ "Codestral (Mistral)": "CODESTRAL_API_KEY", "Groq": "GROQ_API_KEY",
51
+ "Perplexity": "PERPLEXITYAI_API_KEY", "xAI": "XAI_API_KEY",
52
+ "Together AI": "TOGETHERAI_API_KEY", "Fireworks AI": "FIREWORKS_AI_API_KEY",
53
+ "Replicate": "REPLICATE_API_KEY", "Hugging Face": "HUGGINGFACE_API_KEY",
54
+ "Anyscale": "ANYSCALE_API_KEY", "NVIDIA NIM": "NVIDIA_NIM_API_KEY",
55
+ "Deepseek": "DEEPSEEK_API_KEY", "AI21": "AI21_API_KEY",
56
+ "Cerebras": "CEREBRAS_API_KEY", "Moonshot": "MOONSHOT_API_KEY",
57
+ "Ollama": "OLLAMA_API_KEY", "Xinference": "XINFERENCE_API_KEY",
58
+ "Infinity": "INFINITY_API_KEY", "OpenRouter": "OPENROUTER_API_KEY",
59
+ "Deepinfra": "DEEPINFRA_API_KEY", "Cloudflare": "CLOUDFLARE_API_KEY",
60
+ "Baseten": "BASETEN_API_KEY", "Modal": "MODAL_API_KEY",
61
+ "Databricks": "DATABRICKS_API_KEY", "AWS SageMaker": "AWS_ACCESS_KEY_ID",
62
+ "IBM watsonx.ai": "WATSONX_APIKEY", "Predibase": "PREDIBASE_API_KEY",
63
+ "Clarifai": "CLARIFAI_API_KEY", "NLP Cloud": "NLP_CLOUD_API_KEY",
64
+ "Voyage AI": "VOYAGE_API_KEY", "Jina AI": "JINA_API_KEY",
65
+ "Hyperbolic": "HYPERBOLIC_API_KEY", "Morph": "MORPH_API_KEY",
66
+ "Lambda AI": "LAMBDA_API_KEY", "Novita AI": "NOVITA_API_KEY",
67
+ "Aleph Alpha": "ALEPH_ALPHA_API_KEY", "SambaNova": "SAMBANOVA_API_KEY",
68
+ "FriendliAI": "FRIENDLI_TOKEN", "Galadriel": "GALADRIEL_API_KEY",
69
+ "CompactifAI": "COMPACTIFAI_API_KEY", "Lemonade": "LEMONADE_API_KEY",
70
+ "GradientAI": "GRADIENTAI_API_KEY", "Featherless AI": "FEATHERLESS_AI_API_KEY",
71
+ "Nebius AI Studio": "NEBIUS_API_KEY", "Dashscope (Qwen)": "DASHSCOPE_API_KEY",
72
+ "Bytez": "BYTEZ_API_KEY", "Oracle OCI": "OCI_API_KEY",
73
+ "DataRobot": "DATAROBOT_API_KEY", "OVHCloud": "OVHCLOUD_API_KEY",
74
+ "Volcengine": "VOLCENGINE_API_KEY", "Snowflake": "SNOWFLAKE_API_KEY",
75
+ "Nscale": "NSCALE_API_KEY", "Recraft": "RECRAFT_API_KEY",
76
+ "v0": "V0_API_KEY", "Vercel": "VERCEL_AI_GATEWAY_API_KEY",
77
+ "Topaz": "TOPAZ_API_KEY", "ElevenLabs": "ELEVENLABS_API_KEY",
78
+ "Deepgram": "DEEPGRAM_API_KEY", "Custom API": "CUSTOM_API_KEY",
79
+ "GitHub Models": "GITHUB_TOKEN", "GitHub Copilot": "GITHUB_COPILOT_API_KEY",
80
+ }
81
+
82
+ # Discover custom providers and add them to the list
83
+ oauth_providers = {'gemini_cli', 'qwen_code'}
84
+ discovered_providers = {
85
+ p.replace('_', ' ').title(): p.upper() + "_API_KEY"
86
+ for p in PROVIDER_PLUGINS.keys()
87
+ if p not in oauth_providers and p.replace('_', ' ').title() not in LITELLM_PROVIDERS
88
+ }
89
+
90
+ combined_providers = {**LITELLM_PROVIDERS, **discovered_providers}
91
+ provider_display_list = sorted(combined_providers.keys())
92
+
93
+ provider_text = Text()
94
+ for i, provider_name in enumerate(provider_display_list):
95
+ provider_text.append(f" {i + 1}. {provider_name}\n")
96
+
97
+ console.print(Panel(provider_text, title="Available Providers for API Key", style="bold blue"))
98
+
99
+ choice = Prompt.ask(
100
+ Text.from_markup("[bold]Please select a provider or type [red]'b'[/red] to go back[/bold]"),
101
+ choices=[str(i + 1) for i in range(len(provider_display_list))] + ["b"],
102
+ show_choices=False
103
+ )
104
+
105
+ if choice.lower() == 'b':
106
+ return
107
+
108
+ try:
109
+ choice_index = int(choice) - 1
110
+ if 0 <= choice_index < len(provider_display_list):
111
+ display_name = provider_display_list[choice_index]
112
+ api_var_base = combined_providers[display_name]
113
+
114
+ api_key = Prompt.ask(f"Enter the API key for {display_name}")
115
+
116
+ # Special handling for AWS
117
+ if display_name in ["AWS Bedrock", "AWS SageMaker"]:
118
+ console.print(Panel(
119
+ Text.from_markup(
120
+ "This provider requires both an Access Key ID and a Secret Access Key.\n"
121
+ f"The key you entered will be saved as [bold yellow]{api_var_base}_1[/bold yellow].\n"
122
+ "Please manually add the [bold cyan]AWS_SECRET_ACCESS_KEY_1[/bold cyan] to your .env file."
123
+ ),
124
+ title="[bold yellow]Additional Step Required[/bold yellow]",
125
+ border_style="yellow"
126
+ ))
127
+
128
+ key_index = 1
129
+ while True:
130
+ key_name = f"{api_var_base}_{key_index}"
131
+ if ENV_FILE.is_file():
132
+ with open(ENV_FILE, "r") as f:
133
+ if not any(line.startswith(f"{key_name}=") for line in f):
134
+ break
135
+ else:
136
+ break
137
+ key_index += 1
138
+
139
+ key_name = f"{api_var_base}_{key_index}"
140
+ set_key(str(ENV_FILE), key_name, api_key)
141
+
142
+ success_text = Text.from_markup(f"Successfully added {display_name} API key as [bold yellow]'{key_name}'[/bold yellow].")
143
+ console.print(Panel(success_text, style="bold green", title="Success"))
144
+
145
+ else:
146
+ console.print("[bold red]Invalid choice. Please try again.[/bold red]")
147
+ except ValueError:
148
+ console.print("[bold red]Invalid input. Please enter a number or 'b'.[/bold red]")
149
+
150
  async def setup_new_credential(provider_name: str):
151
  """
152
  Interactively sets up a new OAuth credential for a given provider.
 
197
 
198
  async def main():
199
  """
200
+ An interactive CLI tool to add new credentials.
201
  """
202
+ ensure_env_defaults()
203
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
204
 
205
  while True:
206
+ console.print(Panel(
207
+ Text.from_markup("1. Add OAuth Credential\n2. Add API Key"),
208
+ title="Choose credential type",
209
+ style="bold blue"
210
+ ))
211
 
212
+ setup_type = Prompt.ask(
213
+ Text.from_markup("[bold]Please select an option or type [red]'q'[/red] to quit[/bold]"),
214
+ choices=["1", "2", "q"],
 
 
 
 
 
 
215
  show_choices=False
216
  )
217
 
218
+ if setup_type.lower() == 'q':
219
  break
220
 
221
+ if setup_type == "1":
222
+ available_providers = get_available_providers()
223
+ oauth_friendly_names = {
224
+ "gemini_cli": "Gemini CLI (OAuth)",
225
+ "qwen_code": "Qwen Code (OAuth)"
226
+ }
227
+
228
+ provider_text = Text()
229
+ for i, provider in enumerate(available_providers):
230
+ display_name = oauth_friendly_names.get(provider, provider.replace('_', ' ').title())
231
+ provider_text.append(f" {i + 1}. {display_name}\n")
232
+
233
+ console.print(Panel(provider_text, title="Available Providers for OAuth", style="bold blue"))
234
+
235
+ choice = Prompt.ask(
236
+ Text.from_markup("[bold]Please select a provider or type [red]'b'[/red] to go back[/bold]"),
237
+ choices=[str(i + 1) for i in range(len(available_providers))] + ["b"],
238
+ show_choices=False
239
+ )
240
+
241
+ if choice.lower() == 'b':
242
+ continue
243
+
244
+ try:
245
+ choice_index = int(choice) - 1
246
+ if 0 <= choice_index < len(available_providers):
247
+ provider_name = available_providers[choice_index]
248
+ display_name = oauth_friendly_names.get(provider_name, provider_name.replace('_', ' ').title())
249
+ console.print(f"\nStarting OAuth setup for [bold cyan]{display_name}[/bold cyan]...")
250
+ await setup_new_credential(provider_name)
251
+ else:
252
+ console.print("[bold red]Invalid choice. Please try again.[/bold red]")
253
+ except ValueError:
254
+ console.print("[bold red]Invalid input. Please enter a number or 'b'.[/bold red]")
255
+
256
+ elif setup_type == "2":
257
+ await setup_api_key()
258
+
259
  console.print("\n" + "="*50 + "\n")
260
 
261
  def run_credential_tool():