diff --git a/README.md b/README.md index 4bfb2c89..ce338385 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ What makes Presenton different? - Use Fully **self-hosted** in Web through [Docker Package](https://docs.presenton.ai/v3/get-started/quickstart) - Or Download [Desktop App](https://presenton.ai/download) (Mac, Windows & Linux) -- Works with OpenAI, Gemini, Anthropic, Ollama, or custom models +- Works with OpenAI, Gemini, Vertex AI, Azure OpenAI, Anthropic, Ollama, or custom models - Comes with AI Presentation Generation API - Fully open-source (Apache 2.0) - Works with your own design/templates @@ -97,7 +97,7 @@ Presenton gives you complete control over your AI presentation workflow. Choose - Flexible Generation — Build presentations from prompts or uploaded documents - Export Ready — Save as PowerPoint (PPTX) and PDF with professional formatting - Built-In MCP Server — Generate presentations over Model Context Protocol -- Bring Your Own Key — Use your own API keys for OpenAI, Google Gemini, Anthropic Claude, or any compatible provider. Only pay for what you use, no hidden fees or subscriptions. +- Bring Your Own Key — Use your own API keys for OpenAI, Google Gemini, Vertex AI, Azure OpenAI, Anthropic Claude, or any compatible provider. Only pay for what you use, no hidden fees or subscriptions. - Ollama Integration — Run open-source models locally with full privacy - OpenAI API Compatible — Connect to any OpenAI-compatible endpoint with your own models - Multi-Provider Support — Mix and match text and image generation providers @@ -204,11 +204,20 @@ Other optional variables exist in code (for example advanced Mem0 paths, LitePar #### LLM and API keys - **CAN_CHANGE_KEYS**=[true/false]: Set to **false** if you want to keep API keys hidden and make them unmodifiable. -- **LLM**=[openai/google/anthropic/ollama/custom/codex]: Select the text **LLM**. +- **LLM**=[openai/google/vertex/azure/anthropic/ollama/custom/codex]: Select the text **LLM**. - **OPENAI_API_KEY**: Required if **LLM** is **openai**. - **OPENAI_MODEL**: Required if **LLM** is **openai** (default: `gpt-4.1`). - **GOOGLE_API_KEY**: Required if **LLM** is **google**. - **GOOGLE_MODEL**: Required if **LLM** is **google** (default: `models/gemini-2.0-flash`). +- **VERTEX_MODEL**: Required if **LLM** is **vertex** (default: `gemini-2.5-flash`). +- **VERTEX_API_KEY**: Optional auth path for **LLM=vertex** (Vertex Express). +- **VERTEX_PROJECT** / **VERTEX_LOCATION**: Optional auth path for **LLM=vertex** when using GCP project credentials (do not combine with `VERTEX_API_KEY`). +- **VERTEX_BASE_URL**: Optional Vertex gateway/base URL override. +- **AZURE_OPENAI_MODEL**: Required if **LLM** is **azure** (deployment/model name). +- **AZURE_OPENAI_API_KEY**: Required if **LLM** is **azure**. +- **AZURE_OPENAI_API_VERSION**: Required if **LLM** is **azure** (for example `2024-10-21`). +- **AZURE_OPENAI_ENDPOINT** / **AZURE_OPENAI_BASE_URL**: At least one is required if **LLM** is **azure**. +- **AZURE_OPENAI_DEPLOYMENT**: Optional deployment override for **LLM** is **azure**. - **ANTHROPIC_API_KEY**: Required if **LLM** is **anthropic**. - **ANTHROPIC_MODEL**: Required if **LLM** is **anthropic** (default: `claude-3-5-sonnet-20241022`). - **CODEX_MODEL**: Required if **LLM** is **codex** (Codex OAuth flow; compose maps host port **1455** for the callback). @@ -321,6 +330,12 @@ Same variables as compose; use `-e` instead of `.env` when running `docker run` - Using Google
docker run -it --name presenton -p 5000:80 -e LLM="google" -e GOOGLE_API_KEY="******" -e IMAGE_PROVIDER="gemini_flash" -e CAN_CHANGE_KEYS="false" -v "./app_data:/app_data" ghcr.io/presenton/presenton:latest
+- Using Vertex AI (API key mode) +
docker run -it --name presenton -p 5000:80 -e LLM="vertex" -e VERTEX_API_KEY="******" -e VERTEX_MODEL="gemini-2.5-flash" -e IMAGE_PROVIDER="gemini_flash" -e CAN_CHANGE_KEYS="false" -v "./app_data:/app_data" ghcr.io/presenton/presenton:latest
+ +- Using Azure OpenAI +
docker run -it --name presenton -p 5000:80 -e LLM="azure" -e AZURE_OPENAI_API_KEY="******" -e AZURE_OPENAI_MODEL="gpt-4.1" -e AZURE_OPENAI_API_VERSION="2024-10-21" -e AZURE_OPENAI_ENDPOINT="https://YOUR-RESOURCE.openai.azure.com" -e IMAGE_PROVIDER="pexels" -e PEXELS_API_KEY="******" -e CAN_CHANGE_KEYS="false" -v "./app_data:/app_data" ghcr.io/presenton/presenton:latest
+ - Using Ollama
docker run -it --name presenton -p 5000:80 -e LLM="ollama" -e OLLAMA_MODEL="llama3.2:3b" -e IMAGE_PROVIDER="pexels" -e PEXELS_API_KEY="*******" -e CAN_CHANGE_KEYS="false" -v "./app_data:/app_data" ghcr.io/presenton/presenton:latest
diff --git a/docker-compose.yml b/docker-compose.yml index 386cdde7..768278a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,17 @@ services: - OPENAI_MODEL=${OPENAI_MODEL} - GOOGLE_API_KEY=${GOOGLE_API_KEY} - GOOGLE_MODEL=${GOOGLE_MODEL} + - VERTEX_API_KEY=${VERTEX_API_KEY} + - VERTEX_MODEL=${VERTEX_MODEL} + - VERTEX_PROJECT=${VERTEX_PROJECT} + - VERTEX_LOCATION=${VERTEX_LOCATION} + - VERTEX_BASE_URL=${VERTEX_BASE_URL} + - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} + - AZURE_OPENAI_MODEL=${AZURE_OPENAI_MODEL} + - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} + - AZURE_OPENAI_BASE_URL=${AZURE_OPENAI_BASE_URL} + - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} + - AZURE_OPENAI_DEPLOYMENT=${AZURE_OPENAI_DEPLOYMENT} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - ANTHROPIC_MODEL=${ANTHROPIC_MODEL} - OLLAMA_URL=${OLLAMA_URL} @@ -82,6 +93,17 @@ services: - OPENAI_MODEL=${OPENAI_MODEL} - GOOGLE_API_KEY=${GOOGLE_API_KEY} - GOOGLE_MODEL=${GOOGLE_MODEL} + - VERTEX_API_KEY=${VERTEX_API_KEY} + - VERTEX_MODEL=${VERTEX_MODEL} + - VERTEX_PROJECT=${VERTEX_PROJECT} + - VERTEX_LOCATION=${VERTEX_LOCATION} + - VERTEX_BASE_URL=${VERTEX_BASE_URL} + - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} + - AZURE_OPENAI_MODEL=${AZURE_OPENAI_MODEL} + - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} + - AZURE_OPENAI_BASE_URL=${AZURE_OPENAI_BASE_URL} + - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} + - AZURE_OPENAI_DEPLOYMENT=${AZURE_OPENAI_DEPLOYMENT} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - ANTHROPIC_MODEL=${ANTHROPIC_MODEL} - OLLAMA_URL=${OLLAMA_URL} @@ -141,6 +163,17 @@ services: - OPENAI_MODEL=${OPENAI_MODEL} - GOOGLE_API_KEY=${GOOGLE_API_KEY} - GOOGLE_MODEL=${GOOGLE_MODEL} + - VERTEX_API_KEY=${VERTEX_API_KEY} + - VERTEX_MODEL=${VERTEX_MODEL} + - VERTEX_PROJECT=${VERTEX_PROJECT} + - VERTEX_LOCATION=${VERTEX_LOCATION} + - VERTEX_BASE_URL=${VERTEX_BASE_URL} + - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} + - AZURE_OPENAI_MODEL=${AZURE_OPENAI_MODEL} + - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} + - AZURE_OPENAI_BASE_URL=${AZURE_OPENAI_BASE_URL} + - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} + - AZURE_OPENAI_DEPLOYMENT=${AZURE_OPENAI_DEPLOYMENT} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - ANTHROPIC_MODEL=${ANTHROPIC_MODEL} - OLLAMA_URL=${OLLAMA_URL} @@ -205,6 +238,17 @@ services: - OPENAI_MODEL=${OPENAI_MODEL} - GOOGLE_API_KEY=${GOOGLE_API_KEY} - GOOGLE_MODEL=${GOOGLE_MODEL} + - VERTEX_API_KEY=${VERTEX_API_KEY} + - VERTEX_MODEL=${VERTEX_MODEL} + - VERTEX_PROJECT=${VERTEX_PROJECT} + - VERTEX_LOCATION=${VERTEX_LOCATION} + - VERTEX_BASE_URL=${VERTEX_BASE_URL} + - AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY} + - AZURE_OPENAI_MODEL=${AZURE_OPENAI_MODEL} + - AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT} + - AZURE_OPENAI_BASE_URL=${AZURE_OPENAI_BASE_URL} + - AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION} + - AZURE_OPENAI_DEPLOYMENT=${AZURE_OPENAI_DEPLOYMENT} - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} - ANTHROPIC_MODEL=${ANTHROPIC_MODEL} - OLLAMA_URL=${OLLAMA_URL} diff --git a/servers/fastapi/constants/llm.py b/servers/fastapi/constants/llm.py index 1172de4f..cf96920a 100644 --- a/servers/fastapi/constants/llm.py +++ b/servers/fastapi/constants/llm.py @@ -3,5 +3,7 @@ OPENAI_URL = "https://api.openai.com/v1" # Default models DEFAULT_OPENAI_MODEL = "gpt-4.1" DEFAULT_GOOGLE_MODEL = "models/gemini-2.5-flash" +DEFAULT_VERTEX_MODEL = "gemini-2.5-flash" +DEFAULT_AZURE_MODEL = "gpt-4.1" DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-20250514" DEFAULT_CODEX_MODEL = "gpt-5.2" diff --git a/servers/fastapi/enums/llm_provider.py b/servers/fastapi/enums/llm_provider.py index 3bf23f09..c5a98b80 100644 --- a/servers/fastapi/enums/llm_provider.py +++ b/servers/fastapi/enums/llm_provider.py @@ -5,6 +5,8 @@ class LLMProvider(Enum): OLLAMA = "ollama" OPENAI = "openai" GOOGLE = "google" + VERTEX = "vertex" + AZURE = "azure" ANTHROPIC = "anthropic" CUSTOM = "custom" CODEX = "codex" diff --git a/servers/fastapi/models/user_config.py b/servers/fastapi/models/user_config.py index db41401b..c40c1d85 100644 --- a/servers/fastapi/models/user_config.py +++ b/servers/fastapi/models/user_config.py @@ -13,6 +13,21 @@ class UserConfig(BaseModel): GOOGLE_API_KEY: Optional[str] = None GOOGLE_MODEL: Optional[str] = None + # Vertex AI + VERTEX_API_KEY: Optional[str] = None + VERTEX_MODEL: Optional[str] = None + VERTEX_PROJECT: Optional[str] = None + VERTEX_LOCATION: Optional[str] = None + VERTEX_BASE_URL: Optional[str] = None + + # Azure OpenAI + AZURE_OPENAI_API_KEY: Optional[str] = None + AZURE_OPENAI_MODEL: Optional[str] = None + AZURE_OPENAI_ENDPOINT: Optional[str] = None + AZURE_OPENAI_BASE_URL: Optional[str] = None + AZURE_OPENAI_API_VERSION: Optional[str] = None + AZURE_OPENAI_DEPLOYMENT: Optional[str] = None + # Anthropic ANTHROPIC_API_KEY: Optional[str] = None ANTHROPIC_MODEL: Optional[str] = None diff --git a/servers/fastapi/utils/get_env.py b/servers/fastapi/utils/get_env.py index ea111630..cfabbc41 100644 --- a/servers/fastapi/utils/get_env.py +++ b/servers/fastapi/utils/get_env.py @@ -57,6 +57,50 @@ def get_google_model_env(): return os.getenv("GOOGLE_MODEL") +def get_vertex_api_key_env(): + return os.getenv("VERTEX_API_KEY") + + +def get_vertex_model_env(): + return os.getenv("VERTEX_MODEL") + + +def get_vertex_project_env(): + return os.getenv("VERTEX_PROJECT") + + +def get_vertex_location_env(): + return os.getenv("VERTEX_LOCATION") + + +def get_vertex_base_url_env(): + return os.getenv("VERTEX_BASE_URL") + + +def get_azure_openai_api_key_env(): + return os.getenv("AZURE_OPENAI_API_KEY") + + +def get_azure_openai_model_env(): + return os.getenv("AZURE_OPENAI_MODEL") + + +def get_azure_openai_endpoint_env(): + return os.getenv("AZURE_OPENAI_ENDPOINT") + + +def get_azure_openai_base_url_env(): + return os.getenv("AZURE_OPENAI_BASE_URL") + + +def get_azure_openai_api_version_env(): + return os.getenv("AZURE_OPENAI_API_VERSION") + + +def get_azure_openai_deployment_env(): + return os.getenv("AZURE_OPENAI_DEPLOYMENT") + + def get_custom_llm_api_key_env(): return os.getenv("CUSTOM_LLM_API_KEY") diff --git a/servers/fastapi/utils/llm_config.py b/servers/fastapi/utils/llm_config.py index f5340bef..959272f7 100644 --- a/servers/fastapi/utils/llm_config.py +++ b/servers/fastapi/utils/llm_config.py @@ -4,15 +4,22 @@ from typing import Optional from fastapi import HTTPException from llmai.shared import ( AnthropicClientConfig, + AzureOpenAIClientConfig, ChatGPTClientConfig, ClientConfig, GoogleClientConfig, OpenAIApiType, OpenAIClientConfig, + VertexAIClientConfig, ) from enums.llm_provider import LLMProvider from utils.get_env import ( + get_azure_openai_api_key_env, + get_azure_openai_api_version_env, + get_azure_openai_base_url_env, + get_azure_openai_deployment_env, + get_azure_openai_endpoint_env, get_anthropic_api_key_env, get_codex_access_token_env, get_codex_account_id_env, @@ -24,6 +31,10 @@ from utils.get_env import ( get_google_api_key_env, get_ollama_url_env, get_openai_api_key_env, + get_vertex_api_key_env, + get_vertex_base_url_env, + get_vertex_location_env, + get_vertex_project_env, get_web_grounding_env, ) from utils.llm_provider import get_llm_provider @@ -101,6 +112,74 @@ def get_llm_config() -> ClientConfig: if not api_key: raise HTTPException(status_code=400, detail="Google API Key is not set") return GoogleClientConfig(api_key=api_key) + case LLMProvider.VERTEX: + api_key = get_vertex_api_key_env() + project = get_vertex_project_env() + location = get_vertex_location_env() + base_url = get_vertex_base_url_env() + + if api_key and (project or location): + raise HTTPException( + status_code=400, + detail=( + "Vertex configuration is ambiguous. Configure either " + "VERTEX_API_KEY or VERTEX_PROJECT/VERTEX_LOCATION, not both." + ), + ) + + if api_key: + return VertexAIClientConfig( + api_key=api_key, + base_url=base_url or None, + ) + + if not project: + raise HTTPException( + status_code=400, + detail=( + "Vertex configuration is incomplete. Set VERTEX_API_KEY " + "or VERTEX_PROJECT (optionally with VERTEX_LOCATION)." + ), + ) + + return VertexAIClientConfig( + project=project, + location=location or None, + base_url=base_url or None, + ) + case LLMProvider.AZURE: + api_key = get_azure_openai_api_key_env() + api_version = get_azure_openai_api_version_env() + endpoint = get_azure_openai_endpoint_env() + base_url = get_azure_openai_base_url_env() + deployment = get_azure_openai_deployment_env() + + if not api_key: + raise HTTPException( + status_code=400, + detail="Azure OpenAI API Key is not set", + ) + if not api_version: + raise HTTPException( + status_code=400, + detail="Azure OpenAI API Version is not set", + ) + if not endpoint and not base_url: + raise HTTPException( + status_code=400, + detail=( + "Azure OpenAI endpoint is not set. " + "Configure AZURE_OPENAI_ENDPOINT or AZURE_OPENAI_BASE_URL." + ), + ) + + return AzureOpenAIClientConfig( + api_key=api_key, + api_version=api_version, + endpoint=endpoint or None, + base_url=base_url or None, + deployment=deployment or None, + ) case LLMProvider.ANTHROPIC: api_key = get_anthropic_api_key_env() if not api_key: @@ -134,8 +213,8 @@ def get_llm_config() -> ClientConfig: raise HTTPException( status_code=400, detail=( - "LLM Provider must be either openai, google, anthropic, " - "ollama, custom, or codex" + "LLM Provider must be either openai, google, vertex, azure, " + "anthropic, ollama, custom, or codex" ), ) diff --git a/servers/fastapi/utils/llm_provider.py b/servers/fastapi/utils/llm_provider.py index f355d9cc..56e120fd 100644 --- a/servers/fastapi/utils/llm_provider.py +++ b/servers/fastapi/utils/llm_provider.py @@ -4,12 +4,15 @@ from openai import OpenAI from constants.llm import ( DEFAULT_ANTHROPIC_MODEL, + DEFAULT_AZURE_MODEL, DEFAULT_CODEX_MODEL, DEFAULT_GOOGLE_MODEL, DEFAULT_OPENAI_MODEL, + DEFAULT_VERTEX_MODEL, ) from enums.llm_provider import LLMProvider from utils.get_env import ( + get_azure_openai_model_env, get_anthropic_model_env, get_codex_model_env, get_custom_model_env, @@ -19,6 +22,7 @@ from utils.get_env import ( get_ollama_model_env, get_openai_api_key_env, get_openai_model_env, + get_vertex_model_env, ) @@ -28,7 +32,10 @@ def get_llm_provider(): except: raise HTTPException( status_code=500, - detail=f"Invalid LLM provider. Please select one of: openai, google, anthropic, ollama, custom, codex", + detail=( + "Invalid LLM provider. Please select one of: " + "openai, google, vertex, azure, anthropic, ollama, custom, codex" + ), ) @@ -44,6 +51,14 @@ def is_anthropic_selected(): return get_llm_provider() == LLMProvider.ANTHROPIC +def is_vertex_selected(): + return get_llm_provider() == LLMProvider.VERTEX + + +def is_azure_selected(): + return get_llm_provider() == LLMProvider.AZURE + + def is_ollama_selected(): return get_llm_provider() == LLMProvider.OLLAMA @@ -62,6 +77,10 @@ def get_model(): return get_openai_model_env() or DEFAULT_OPENAI_MODEL elif selected_llm == LLMProvider.GOOGLE: return get_google_model_env() or DEFAULT_GOOGLE_MODEL + elif selected_llm == LLMProvider.VERTEX: + return get_vertex_model_env() or DEFAULT_VERTEX_MODEL + elif selected_llm == LLMProvider.AZURE: + return get_azure_openai_model_env() or DEFAULT_AZURE_MODEL elif selected_llm == LLMProvider.ANTHROPIC: return get_anthropic_model_env() or DEFAULT_ANTHROPIC_MODEL elif selected_llm == LLMProvider.OLLAMA: @@ -73,7 +92,10 @@ def get_model(): else: raise HTTPException( status_code=500, - detail=f"Invalid LLM provider. Please select one of: openai, google, anthropic, ollama, custom, codex", + detail=( + "Invalid LLM provider. Please select one of: " + "openai, google, vertex, azure, anthropic, ollama, custom, codex" + ), ) diff --git a/servers/fastapi/utils/llm_utils.py b/servers/fastapi/utils/llm_utils.py index 027decc9..7de18ef5 100644 --- a/servers/fastapi/utils/llm_utils.py +++ b/servers/fastapi/utils/llm_utils.py @@ -64,7 +64,7 @@ def get_generate_kwargs( if max_tokens is not None: kwargs["max_tokens"] = max_tokens if tools: - if get_llm_provider() == LLMProvider.GOOGLE: + if get_llm_provider() in (LLMProvider.GOOGLE, LLMProvider.VERTEX): kwargs["tools"] = _tools_for_google_gemini(tools) else: kwargs["tools"] = tools diff --git a/servers/fastapi/utils/model_availability.py b/servers/fastapi/utils/model_availability.py index 1c40070d..e17e0bf9 100644 --- a/servers/fastapi/utils/model_availability.py +++ b/servers/fastapi/utils/model_availability.py @@ -8,6 +8,10 @@ from utils.available_models import ( list_available_openai_compatible_models, ) from utils.get_env import ( + get_azure_openai_api_key_env, + get_azure_openai_api_version_env, + get_azure_openai_base_url_env, + get_azure_openai_endpoint_env, get_anthropic_api_key_env, get_anthropic_model_env, get_can_change_keys_env, @@ -16,6 +20,9 @@ from utils.get_env import ( get_openai_model_env, get_pixabay_api_key_env, get_pexels_api_key_env, + get_vertex_api_key_env, + get_vertex_location_env, + get_vertex_project_env, get_comfyui_url_env, get_comfyui_workflow_env, ) @@ -65,6 +72,34 @@ async def check_llm_and_image_provider_api_or_model_availability(): print("Available models: ", available_models) raise Exception(f"Model {google_model} is not available") + elif get_llm_provider() == LLMProvider.VERTEX: + vertex_api_key = get_vertex_api_key_env() + vertex_project = get_vertex_project_env() + vertex_location = get_vertex_location_env() + if not vertex_api_key and not vertex_project: + raise Exception( + "Configure VERTEX_API_KEY or VERTEX_PROJECT for Vertex AI" + ) + if vertex_api_key and (vertex_project or vertex_location): + raise Exception( + "Vertex config is ambiguous. Use either VERTEX_API_KEY or " + "VERTEX_PROJECT/VERTEX_LOCATION, not both." + ) + + elif get_llm_provider() == LLMProvider.AZURE: + azure_api_key = get_azure_openai_api_key_env() + azure_endpoint = get_azure_openai_endpoint_env() + azure_base_url = get_azure_openai_base_url_env() + azure_api_version = get_azure_openai_api_version_env() + if not azure_api_key: + raise Exception("AZURE_OPENAI_API_KEY must be provided") + if not azure_api_version: + raise Exception("AZURE_OPENAI_API_VERSION must be provided") + if not azure_endpoint and not azure_base_url: + raise Exception( + "AZURE_OPENAI_ENDPOINT or AZURE_OPENAI_BASE_URL must be provided" + ) + elif get_llm_provider() == LLMProvider.ANTHROPIC: anthropic_api_key = get_anthropic_api_key_env() if not anthropic_api_key: diff --git a/servers/fastapi/utils/set_env.py b/servers/fastapi/utils/set_env.py index 18456d8e..93ca0e1e 100644 --- a/servers/fastapi/utils/set_env.py +++ b/servers/fastapi/utils/set_env.py @@ -37,6 +37,50 @@ def set_google_model_env(value): os.environ["GOOGLE_MODEL"] = value +def set_vertex_api_key_env(value): + os.environ["VERTEX_API_KEY"] = value + + +def set_vertex_model_env(value): + os.environ["VERTEX_MODEL"] = value + + +def set_vertex_project_env(value): + os.environ["VERTEX_PROJECT"] = value + + +def set_vertex_location_env(value): + os.environ["VERTEX_LOCATION"] = value + + +def set_vertex_base_url_env(value): + os.environ["VERTEX_BASE_URL"] = value + + +def set_azure_openai_api_key_env(value): + os.environ["AZURE_OPENAI_API_KEY"] = value + + +def set_azure_openai_model_env(value): + os.environ["AZURE_OPENAI_MODEL"] = value + + +def set_azure_openai_endpoint_env(value): + os.environ["AZURE_OPENAI_ENDPOINT"] = value + + +def set_azure_openai_base_url_env(value): + os.environ["AZURE_OPENAI_BASE_URL"] = value + + +def set_azure_openai_api_version_env(value): + os.environ["AZURE_OPENAI_API_VERSION"] = value + + +def set_azure_openai_deployment_env(value): + os.environ["AZURE_OPENAI_DEPLOYMENT"] = value + + def set_anthropic_api_key_env(value): os.environ["ANTHROPIC_API_KEY"] = value diff --git a/servers/fastapi/utils/user_config.py b/servers/fastapi/utils/user_config.py index bc499075..876a7362 100644 --- a/servers/fastapi/utils/user_config.py +++ b/servers/fastapi/utils/user_config.py @@ -15,6 +15,17 @@ from utils.get_env import ( get_disable_thinking_env, get_google_api_key_env, get_google_model_env, + get_vertex_api_key_env, + get_vertex_model_env, + get_vertex_project_env, + get_vertex_location_env, + get_vertex_base_url_env, + get_azure_openai_api_key_env, + get_azure_openai_model_env, + get_azure_openai_endpoint_env, + get_azure_openai_base_url_env, + get_azure_openai_api_version_env, + get_azure_openai_deployment_env, get_gpt_image_1_5_quality_env, get_llm_provider_env, get_ollama_model_env, @@ -53,6 +64,17 @@ from utils.set_env import ( set_extended_reasoning_env, set_google_api_key_env, set_google_model_env, + set_vertex_api_key_env, + set_vertex_model_env, + set_vertex_project_env, + set_vertex_location_env, + set_vertex_base_url_env, + set_azure_openai_api_key_env, + set_azure_openai_model_env, + set_azure_openai_endpoint_env, + set_azure_openai_base_url_env, + set_azure_openai_api_version_env, + set_azure_openai_deployment_env, set_gpt_image_1_5_quality_env, set_llm_provider_env, set_ollama_model_env, @@ -94,6 +116,23 @@ def get_user_config(): OPENAI_MODEL=existing_config.OPENAI_MODEL or get_openai_model_env(), GOOGLE_API_KEY=existing_config.GOOGLE_API_KEY or get_google_api_key_env(), GOOGLE_MODEL=existing_config.GOOGLE_MODEL or get_google_model_env(), + VERTEX_API_KEY=existing_config.VERTEX_API_KEY or get_vertex_api_key_env(), + VERTEX_MODEL=existing_config.VERTEX_MODEL or get_vertex_model_env(), + VERTEX_PROJECT=existing_config.VERTEX_PROJECT or get_vertex_project_env(), + VERTEX_LOCATION=existing_config.VERTEX_LOCATION or get_vertex_location_env(), + VERTEX_BASE_URL=existing_config.VERTEX_BASE_URL or get_vertex_base_url_env(), + AZURE_OPENAI_API_KEY=existing_config.AZURE_OPENAI_API_KEY + or get_azure_openai_api_key_env(), + AZURE_OPENAI_MODEL=existing_config.AZURE_OPENAI_MODEL + or get_azure_openai_model_env(), + AZURE_OPENAI_ENDPOINT=existing_config.AZURE_OPENAI_ENDPOINT + or get_azure_openai_endpoint_env(), + AZURE_OPENAI_BASE_URL=existing_config.AZURE_OPENAI_BASE_URL + or get_azure_openai_base_url_env(), + AZURE_OPENAI_API_VERSION=existing_config.AZURE_OPENAI_API_VERSION + or get_azure_openai_api_version_env(), + AZURE_OPENAI_DEPLOYMENT=existing_config.AZURE_OPENAI_DEPLOYMENT + or get_azure_openai_deployment_env(), ANTHROPIC_API_KEY=existing_config.ANTHROPIC_API_KEY or get_anthropic_api_key_env(), ANTHROPIC_MODEL=existing_config.ANTHROPIC_MODEL or get_anthropic_model_env(), @@ -160,6 +199,28 @@ def update_env_with_user_config(): set_google_api_key_env(user_config.GOOGLE_API_KEY) if user_config.GOOGLE_MODEL: set_google_model_env(user_config.GOOGLE_MODEL) + if user_config.VERTEX_API_KEY: + set_vertex_api_key_env(user_config.VERTEX_API_KEY) + if user_config.VERTEX_MODEL: + set_vertex_model_env(user_config.VERTEX_MODEL) + if user_config.VERTEX_PROJECT: + set_vertex_project_env(user_config.VERTEX_PROJECT) + if user_config.VERTEX_LOCATION: + set_vertex_location_env(user_config.VERTEX_LOCATION) + if user_config.VERTEX_BASE_URL: + set_vertex_base_url_env(user_config.VERTEX_BASE_URL) + if user_config.AZURE_OPENAI_API_KEY: + set_azure_openai_api_key_env(user_config.AZURE_OPENAI_API_KEY) + if user_config.AZURE_OPENAI_MODEL: + set_azure_openai_model_env(user_config.AZURE_OPENAI_MODEL) + if user_config.AZURE_OPENAI_ENDPOINT: + set_azure_openai_endpoint_env(user_config.AZURE_OPENAI_ENDPOINT) + if user_config.AZURE_OPENAI_BASE_URL: + set_azure_openai_base_url_env(user_config.AZURE_OPENAI_BASE_URL) + if user_config.AZURE_OPENAI_API_VERSION: + set_azure_openai_api_version_env(user_config.AZURE_OPENAI_API_VERSION) + if user_config.AZURE_OPENAI_DEPLOYMENT: + set_azure_openai_deployment_env(user_config.AZURE_OPENAI_DEPLOYMENT) if user_config.ANTHROPIC_API_KEY: set_anthropic_api_key_env(user_config.ANTHROPIC_API_KEY) if user_config.ANTHROPIC_MODEL: diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx index 1c027d7a..e25dda74 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/SettingPage.tsx @@ -289,6 +289,10 @@ const SettingsPage = () => { ? llmConfig.OPENAI_MODEL : textProviderKey === "google" ? llmConfig.GOOGLE_MODEL + : textProviderKey === "vertex" + ? llmConfig.VERTEX_MODEL + : textProviderKey === "azure" + ? llmConfig.AZURE_OPENAI_MODEL : textProviderKey === "anthropic" ? llmConfig.ANTHROPIC_MODEL : textProviderKey === "ollama" @@ -312,7 +316,7 @@ const SettingsPage = () => { useEffect(() => { - if (llmConfig.LLM === "codex" && !llmConfig.CODEX_MODEL || llmConfig.LLM === "openai" && !llmConfig.OPENAI_MODEL || llmConfig.LLM === "google" && !llmConfig.GOOGLE_MODEL || llmConfig.LLM === "anthropic" && !llmConfig.ANTHROPIC_MODEL || llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL || llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL) { + if (llmConfig.LLM === "codex" && !llmConfig.CODEX_MODEL || llmConfig.LLM === "openai" && !llmConfig.OPENAI_MODEL || llmConfig.LLM === "google" && !llmConfig.GOOGLE_MODEL || llmConfig.LLM === "vertex" && !llmConfig.VERTEX_MODEL || llmConfig.LLM === "azure" && !llmConfig.AZURE_OPENAI_MODEL || llmConfig.LLM === "anthropic" && !llmConfig.ANTHROPIC_MODEL || llmConfig.LLM === "ollama" && !llmConfig.OLLAMA_MODEL || llmConfig.LLM === "custom" && !llmConfig.CUSTOM_MODEL) { notify.error("Cannot save settings", "Please select a model for the selected provider"); const currentUrl = window.location.href; diff --git a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx index 09e93533..59d82932 100644 --- a/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx +++ b/servers/nextjs/app/(presentation-generator)/(dashboard)/settings/TextProvider.tsx @@ -23,6 +23,8 @@ interface ModelOption { size?: string; } +const MANUAL_MODEL_PROVIDERS = new Set(['vertex', 'azure']); + const TextProvider = ({ onInputChange, @@ -40,12 +42,17 @@ const TextProvider = ({ const selectedProvider = (llmConfig.LLM || 'openai') as keyof typeof LLM_PROVIDERS; const selectedProviderMeta = LLM_PROVIDERS[selectedProvider]; + const isManualModelProvider = MANUAL_MODEL_PROVIDERS.has(selectedProvider); const currentModelField = useMemo(() => { switch (selectedProvider) { case 'openai': return 'OPENAI_MODEL'; case 'google': return 'GOOGLE_MODEL'; + case 'vertex': + return 'VERTEX_MODEL'; + case 'azure': + return 'AZURE_OPENAI_MODEL'; case 'anthropic': return 'ANTHROPIC_MODEL'; case 'ollama': @@ -65,6 +72,10 @@ const TextProvider = ({ return 'OPENAI_API_KEY'; case 'google': return 'GOOGLE_API_KEY'; + case 'vertex': + return 'VERTEX_API_KEY'; + case 'azure': + return 'AZURE_OPENAI_API_KEY'; case 'anthropic': return 'ANTHROPIC_API_KEY'; case 'custom': @@ -80,6 +91,14 @@ const TextProvider = ({ const currentOllamaUrl = llmConfig.OLLAMA_URL || ''; const useCustomOllamaUrl = !!llmConfig.USE_CUSTOM_URL; const modelLabel = selectedProviderMeta?.label || selectedProvider; + const providerApiKeyLabel = + selectedProvider === 'custom' + ? 'Custom LLM API Key' + : selectedProvider === 'vertex' + ? 'Vertex API Key' + : selectedProvider === 'azure' + ? 'Azure OpenAI API Key' + : `${selectedProvider} API Key`; useEffect(() => { if (isFirstRender.current) { @@ -107,6 +126,10 @@ const TextProvider = ({ ? 'OPENAI_API_KEY' : llm === 'google' ? 'GOOGLE_API_KEY' + : llm === 'vertex' + ? 'VERTEX_API_KEY' + : llm === 'azure' + ? 'AZURE_OPENAI_API_KEY' : llm === 'anthropic' ? 'ANTHROPIC_API_KEY' : llm === 'custom' @@ -118,6 +141,7 @@ const TextProvider = ({ }; const fetchAvailableModels = async () => { + if (isManualModelProvider) return; if (selectedProvider === 'openai' && !currentApiKey) return; if (selectedProvider === 'google' && !currentApiKey) return; if (selectedProvider === 'anthropic' && !currentApiKey) return; @@ -405,7 +429,7 @@ const TextProvider = ({ : ( <>
onApiKeyChange(selectedProvider, e.target.value)} className="w-full px-2 py-3 outline-none border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-colors" - placeholder={`Enter your ${llmConfig.LLM} API key`} + placeholder={`Enter your ${providerApiKeyLabel}`} />
- {selectedProvider !== 'ollama' && selectedProvider !== 'codex' && (!modelsChecked || (modelsChecked && availableModels.length === 0)) && ( + {!isManualModelProvider && selectedProvider !== 'ollama' && selectedProvider !== 'codex' && (!modelsChecked || (modelsChecked && availableModels.length === 0)) && (