From 976908642b76e876a39632c336ae53c9e6a6142a Mon Sep 17 00:00:00 2001 From: Jefferson Phillips <13530594+Jefferderp@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:49:52 -0400 Subject: [PATCH] Implement API key usage --- .env.example | 1 + README.md | 5 ++++- app.py | 34 +++++++++++++++++++++++++++++++--- tts_engine/inference.py | 14 ++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 11ad5da..ade9bf8 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,7 @@ # Server connection settings ORPHEUS_API_URL=http://127.0.0.1:1234/v1/completions ORPHEUS_API_TIMEOUT=120 # You should scale this value based on max tokens and your inference speed +ORPHEUS_API_KEY=your_api_key_here # API key for authentication with the OpenAI-compatible API # Generation parameters ORPHEUS_MAX_TOKENS=8192 # If you want longer completions, increase this value diff --git a/README.md b/README.md index edaf2ef..813fcef 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ The server provides an OpenAI-compatible API endpoint at `/v1/audio/speech`: ```bash curl http://localhost:5005/v1/audio/speech \ -H "Content-Type: application/json" \ + -H "Authorization: Bearer your_api_key_here" \ -d '{ "model": "orpheus", "input": "Hello world! This is a test of the Orpheus TTS system.", @@ -165,6 +166,7 @@ Additionally, a simpler `/speak` endpoint is available: ```bash curl -X POST http://localhost:5005/speak \ -H "Content-Type: application/json" \ + -H "Authorization: Bearer your_api_key_here" \ -d '{ "text": "Hello world! This is a test.", "voice": "tara" @@ -265,7 +267,7 @@ You can easily integrate this TTS solution with [OpenWebUI](https://github.com/o 2. In OpenWebUI, go to Admin Panel > Settings > Audio 3. Change TTS from Web API to OpenAI 4. Set APIBASE URL to your server address (e.g., `http://localhost:5005/v1`) -5. API Key can be set to "not-needed" +5. Set API Key to your configured API key or "not-needed" if API key authentication is not enabled 6. Set TTS Voice to one of the available voices: `tara`, `leah`, `jess`, `leo`, `dan`, `mia`, `zac`, or `zoe` 7. Set TTS Model to `tts-1` @@ -295,6 +297,7 @@ You can configure the system using environment variables or a `.env` file: - `ORPHEUS_API_URL`: URL of the LLM inference API (tts_engine/inference.py) - `ORPHEUS_API_TIMEOUT`: Timeout in seconds for API requests (default: 120) +- `ORPHEUS_API_KEY`: API key for authentication with the OpenAI-compatible API (optional) - `ORPHEUS_MAX_TOKENS`: Maximum tokens to generate (default: 8192) - `ORPHEUS_TEMPERATURE`: Temperature for generation (default: 0.6) - `ORPHEUS_TOP_P`: Top-p sampling parameter (default: 0.9) diff --git a/app.py b/app.py index 549c651..9050594 100644 --- a/app.py +++ b/app.py @@ -7,6 +7,7 @@ import time import asyncio from datetime import datetime from typing import List, Optional +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from dotenv import load_dotenv # Function to ensure .env file exists @@ -28,7 +29,7 @@ ensure_env_file_exists() # Load environment variables from .env file load_dotenv(override=True) -from fastapi import FastAPI, Request, Form, HTTPException, Depends +from fastapi import FastAPI, Request, Form, HTTPException, Depends, Security from fastapi.responses import HTMLResponse, FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates @@ -59,6 +60,33 @@ app.mount("/static", StaticFiles(directory="static"), name="static") # Setup templates templates = Jinja2Templates(directory="templates") +# API key authentication +security = HTTPBearer(auto_error=False) + +# Get API key from environment +API_KEY = os.environ.get("ORPHEUS_API_KEY") + +# Function to verify API key +async def verify_api_key(credentials: HTTPAuthorizationCredentials = Security(security)): + """ + Verify the API key from the Authorization header. + If no API key is configured, authentication is skipped. + """ + # If no API key is configured, skip authentication + if not API_KEY: + return True + + # If API key is configured but no credentials provided, raise 401 + if not credentials: + raise HTTPException( + status_code=401, + detail="Missing API key. Please provide an API key in the Authorization header." + ) + + # Verify the API key + if credentials.credentials != API_KEY: + raise HTTPException(status_code=401, detail="Invalid API key") + # API models class SpeechRequest(BaseModel): input: str @@ -75,7 +103,7 @@ class APIResponse(BaseModel): # OpenAI-compatible API endpoint @app.post("/v1/audio/speech") -async def create_speech_api(request: SpeechRequest): +async def create_speech_api(request: SpeechRequest, authorized: bool = Depends(verify_api_key)): """ Generate speech from text using the Orpheus TTS model. Compatible with OpenAI's /v1/audio/speech endpoint. @@ -116,7 +144,7 @@ async def create_speech_api(request: SpeechRequest): # Legacy API endpoint for compatibility @app.post("/speak") -async def speak(request: Request): +async def speak(request: Request, authorized: bool = Depends(verify_api_key)): """Legacy endpoint for compatibility with existing clients""" data = await request.json() text = data.get("text", "") diff --git a/tts_engine/inference.py b/tts_engine/inference.py index 7e1e501..ce86401 100644 --- a/tts_engine/inference.py +++ b/tts_engine/inference.py @@ -85,9 +85,17 @@ API_URL = os.environ.get("ORPHEUS_API_URL") if not API_URL: print("WARNING: ORPHEUS_API_URL not set. API calls will fail until configured.") +# Get API key from environment +API_KEY = os.environ.get("ORPHEUS_API_KEY") +if not API_KEY: + print("WARNING: ORPHEUS_API_KEY not set. API calls may fail if the endpoint requires authentication.") + +# Set up headers with authentication if API key is provided HEADERS = { "Content-Type": "application/json" } +if API_KEY: + HEADERS["Authorization"] = f"Bearer {API_KEY}" # Request timeout settings try: @@ -252,6 +260,12 @@ def generate_tokens_from_api(prompt: str, voice: str = DEFAULT_VOICE, temperatur if response.status_code != 200: print(f"Error: API request failed with status code {response.status_code}") + if response.status_code == 401: + print(f"Authentication failed. Please check your API key in the .env file.") + return + elif response.status_code == 403: + print(f"Authorization failed. Your API key may not have access to this resource.") + return print(f"Error details: {response.text}") # Retry on server errors (5xx) but not on client errors (4xx) if response.status_code >= 500: