Mirrowel commited on
Commit
34e560e
·
1 Parent(s): cf0afec

feat(auth): add interactive onboarding and credential setup with warnings

Browse files

- add first-time onboarding checks in proxy_app/main.py to detect missing .env or PROXY_API_KEY and display a clear rich-powered onboarding message
- auto-launch the credential setup tool (ensure_env_defaults + run_credential_tool) and reload environment vars, with validation to prevent starting without PROXY_API_KEY
- add runtime warning when no provider credentials are configured so the proxy can start but not serve LLM requests
- change RotatingClient to log a warning instead of raising when no API keys or OAuth credentials are provided
- clear the console when the credential tool starts and exits for a cleaner interactive experience

src/proxy_app/main.py CHANGED
@@ -337,6 +337,17 @@ async def lifespan(app: FastAPI):
337
  )
338
  client.background_refresher.start() # Start the background task
339
  app.state.rotating_client = client
 
 
 
 
 
 
 
 
 
 
 
340
  os.environ["LITELLM_LOG"] = "ERROR"
341
  litellm.set_verbose = False
342
  litellm.drop_params = True
@@ -747,6 +758,56 @@ async def token_count(
747
  raise HTTPException(status_code=500, detail=str(e))
748
 
749
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
750
  if args.add_credential:
751
  # Import and call ensure_env_defaults to create .env and PROXY_API_KEY if needed
752
  from rotator_library.credential_tool import ensure_env_defaults
@@ -755,8 +816,41 @@ if __name__ == "__main__":
755
  load_dotenv(override=True)
756
  run_credential_tool()
757
  else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  # Validate PROXY_API_KEY before starting the server
759
  if not PROXY_API_KEY:
760
  raise ValueError("PROXY_API_KEY environment variable not set. Please run with --add-credential to set up your environment.")
 
761
  import uvicorn
762
  uvicorn.run(app, host=args.host, port=args.port)
 
 
 
337
  )
338
  client.background_refresher.start() # Start the background task
339
  app.state.rotating_client = client
340
+
341
+ # Warn if no provider credentials are configured
342
+ if not client.all_credentials:
343
+ logging.warning("=" * 70)
344
+ logging.warning("⚠️ NO PROVIDER CREDENTIALS CONFIGURED")
345
+ logging.warning("The proxy is running but cannot serve any LLM requests.")
346
+ logging.warning("Launch the credential tool to add API keys or OAuth credentials.")
347
+ logging.warning(" • Executable: Run with --add-credential flag")
348
+ logging.warning(" • Source: python src/proxy_app/main.py --add-credential")
349
+ logging.warning("=" * 70)
350
+
351
  os.environ["LITELLM_LOG"] = "ERROR"
352
  litellm.set_verbose = False
353
  litellm.drop_params = True
 
758
  raise HTTPException(status_code=500, detail=str(e))
759
 
760
  if __name__ == "__main__":
761
+ # Define ENV_FILE for onboarding checks
762
+ ENV_FILE = Path.cwd() / ".env"
763
+
764
+ def needs_onboarding() -> bool:
765
+ """
766
+ Check if the proxy needs onboarding (first-time setup).
767
+ Returns True if onboarding is needed, False otherwise.
768
+ """
769
+ # Check 1: Does .env file exist?
770
+ if not ENV_FILE.is_file():
771
+ return True
772
+
773
+ # Check 2: Is PROXY_API_KEY set in environment?
774
+ if not PROXY_API_KEY:
775
+ return True
776
+
777
+ return False
778
+
779
+ def show_onboarding_message():
780
+ """Display clear explanatory message for why onboarding is needed."""
781
+ console.clear() # Clear terminal for clean presentation
782
+ console.print(Panel.fit(
783
+ "[bold cyan]🚀 LLM API Key Proxy - First Time Setup[/bold cyan]",
784
+ border_style="cyan"
785
+ ))
786
+ console.print("[bold yellow]⚠️ Configuration Required[/bold yellow]\n")
787
+
788
+ console.print("The proxy cannot start because:")
789
+ if not ENV_FILE.is_file():
790
+ console.print(" [red]❌ No .env file found[/red]")
791
+ else:
792
+ console.print(" [red]❌ PROXY_API_KEY is not set in .env[/red]")
793
+
794
+ console.print("\n[bold]Why this matters:[/bold]")
795
+ console.print(" • The .env file stores your proxy's authentication key")
796
+ console.print(" • The PROXY_API_KEY protects your proxy from unauthorized access")
797
+ console.print(" • Without it, the proxy cannot securely start")
798
+
799
+ console.print("\n[bold]What happens next:[/bold]")
800
+ console.print(" 1. We'll create a .env file with a default PROXY_API_KEY")
801
+ console.print(" 2. You can add LLM provider credentials (API keys or OAuth)")
802
+ console.print(" 3. The proxy will then start normally")
803
+
804
+ console.print("\n[bold yellow]⚠️ Important:[/bold yellow] While provider credentials are optional for startup,")
805
+ console.print(" the proxy won't do anything useful without them. See [bold cyan]README.md[/bold cyan]")
806
+ console.print(" for supported providers and setup instructions.\n")
807
+
808
+ console.input("[bold green]Press Enter to launch the credential setup tool...[/bold green]")
809
+
810
+ # Check if user explicitly wants to add credentials
811
  if args.add_credential:
812
  # Import and call ensure_env_defaults to create .env and PROXY_API_KEY if needed
813
  from rotator_library.credential_tool import ensure_env_defaults
 
816
  load_dotenv(override=True)
817
  run_credential_tool()
818
  else:
819
+ # Check if onboarding is needed
820
+ if needs_onboarding():
821
+ # Import console from rich for better messaging
822
+ from rich.console import Console
823
+ from rich.panel import Panel
824
+ console = Console()
825
+
826
+ # Show clear explanatory message
827
+ show_onboarding_message()
828
+
829
+ # Launch credential tool automatically
830
+ from rotator_library.credential_tool import ensure_env_defaults
831
+ ensure_env_defaults()
832
+ load_dotenv(override=True)
833
+ run_credential_tool()
834
+
835
+ # After credential tool exits, reload and re-check
836
+ load_dotenv(override=True)
837
+ # Re-read PROXY_API_KEY from environment
838
+ PROXY_API_KEY = os.getenv("PROXY_API_KEY")
839
+
840
+ # Verify onboarding is complete
841
+ if needs_onboarding():
842
+ console.print("\n[bold red]❌ Configuration incomplete.[/bold red]")
843
+ console.print("The proxy still cannot start. Please ensure PROXY_API_KEY is set in .env\n")
844
+ sys.exit(1)
845
+ else:
846
+ console.print("\n[bold green]✅ Configuration complete![/bold green]")
847
+ console.print("\nStarting proxy server...\n")
848
+
849
  # Validate PROXY_API_KEY before starting the server
850
  if not PROXY_API_KEY:
851
  raise ValueError("PROXY_API_KEY environment variable not set. Please run with --add-credential to set up your environment.")
852
+
853
  import uvicorn
854
  uvicorn.run(app, host=args.host, port=args.port)
855
+
856
+
src/rotator_library/client.py CHANGED
@@ -88,8 +88,8 @@ class RotatingClient:
88
  }
89
 
90
  if not api_keys and not oauth_credentials:
91
- raise ValueError(
92
- "No valid credentials provided. Either 'api_keys' or 'oauth_credentials' must be provided and non-empty."
93
  )
94
 
95
  self.api_keys = api_keys
 
88
  }
89
 
90
  if not api_keys and not oauth_credentials:
91
+ lib_logger.warning(
92
+ "No provider credentials configured. The client will be unable to make any API requests."
93
  )
94
 
95
  self.api_keys = api_keys
src/rotator_library/credential_tool.py CHANGED
@@ -515,6 +515,7 @@ async def main():
515
  """
516
  An interactive CLI tool to add new credentials.
517
  """
 
518
  ensure_env_defaults()
519
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
520
 
@@ -593,5 +594,7 @@ async def main():
593
  def run_credential_tool():
594
  try:
595
  asyncio.run(main())
 
596
  except KeyboardInterrupt:
597
- console.print("\n[bold yellow]Exiting setup.[/bold yellow]")
 
 
515
  """
516
  An interactive CLI tool to add new credentials.
517
  """
518
+ console.clear() # Clear terminal when credential tool starts
519
  ensure_env_defaults()
520
  console.print(Panel("[bold cyan]Interactive Credential Setup[/bold cyan]", title="--- API Key Proxy ---", expand=False))
521
 
 
594
  def run_credential_tool():
595
  try:
596
  asyncio.run(main())
597
+ console.clear() # Clear terminal when credential tool exits
598
  except KeyboardInterrupt:
599
+ console.print("\n[bold yellow]Exiting setup.[/bold yellow]")
600
+ console.clear() # Clear terminal on keyboard interrupt too