Add support for Anthropic, Ollama, and Watsonx config
Introduces fields and validation for Anthropic API key, Ollama endpoint, and IBM watsonx.ai API key, endpoint, and project ID in environment management and configuration screens. Updates validation utilities and config UI to support these providers, allowing users to set and validate credentials and endpoints for additional AI services.
This commit is contained in:
parent
e23a7d0f59
commit
933e600e9d
3 changed files with 237 additions and 4 deletions
|
|
@ -35,6 +35,13 @@ class EnvConfig:
|
|||
langflow_ingest_flow_id: str = "5488df7c-b93f-4f87-a446-b67028bc0813"
|
||||
langflow_url_ingest_flow_id: str = "72c3d17c-2dac-4a73-b48a-6518473d7830"
|
||||
|
||||
# Provider API keys and endpoints
|
||||
anthropic_api_key: str = ""
|
||||
ollama_endpoint: str = ""
|
||||
watsonx_api_key: str = ""
|
||||
watsonx_endpoint: str = ""
|
||||
watsonx_project_id: str = ""
|
||||
|
||||
# OAuth settings
|
||||
google_oauth_client_id: str = ""
|
||||
google_oauth_client_secret: str = ""
|
||||
|
|
@ -128,6 +135,11 @@ class EnvManager:
|
|||
# Map env vars to config attributes
|
||||
attr_map = {
|
||||
"OPENAI_API_KEY": "openai_api_key",
|
||||
"ANTHROPIC_API_KEY": "anthropic_api_key",
|
||||
"OLLAMA_ENDPOINT": "ollama_endpoint",
|
||||
"WATSONX_API_KEY": "watsonx_api_key",
|
||||
"WATSONX_ENDPOINT": "watsonx_endpoint",
|
||||
"WATSONX_PROJECT_ID": "watsonx_project_id",
|
||||
"OPENSEARCH_PASSWORD": "opensearch_password",
|
||||
"LANGFLOW_SECRET_KEY": "langflow_secret_key",
|
||||
"LANGFLOW_SUPERUSER": "langflow_superuser",
|
||||
|
|
@ -197,6 +209,30 @@ class EnvManager:
|
|||
"Invalid OpenAI API key format (should start with sk-)"
|
||||
)
|
||||
|
||||
# Import validation functions for new provider fields
|
||||
from ..utils.validation import validate_anthropic_api_key
|
||||
|
||||
# Validate Anthropic API key format if provided
|
||||
if self.config.anthropic_api_key:
|
||||
if not validate_anthropic_api_key(self.config.anthropic_api_key):
|
||||
self.config.validation_errors["anthropic_api_key"] = (
|
||||
"Invalid Anthropic API key format (should start with sk-ant-)"
|
||||
)
|
||||
|
||||
# Validate Ollama endpoint if provided
|
||||
if self.config.ollama_endpoint:
|
||||
if not validate_url(self.config.ollama_endpoint):
|
||||
self.config.validation_errors["ollama_endpoint"] = (
|
||||
"Invalid Ollama endpoint URL format"
|
||||
)
|
||||
|
||||
# Validate IBM watsonx.ai endpoint if provided
|
||||
if self.config.watsonx_endpoint:
|
||||
if not validate_url(self.config.watsonx_endpoint):
|
||||
self.config.validation_errors["watsonx_endpoint"] = (
|
||||
"Invalid IBM watsonx.ai endpoint URL format"
|
||||
)
|
||||
|
||||
# Validate documents paths only if provided (optional)
|
||||
if self.config.openrag_documents_paths:
|
||||
is_valid, error_msg, _ = validate_documents_paths(
|
||||
|
|
@ -289,9 +325,6 @@ class EnvManager:
|
|||
f.write(f"LANGFLOW_URL_INGEST_FLOW_ID={self._quote_env_value(self.config.langflow_url_ingest_flow_id)}\n")
|
||||
f.write(f"NUDGES_FLOW_ID={self._quote_env_value(self.config.nudges_flow_id)}\n")
|
||||
f.write(f"OPENSEARCH_PASSWORD={self._quote_env_value(self.config.opensearch_password)}\n")
|
||||
# Only write OpenAI API key if provided (can be set during onboarding instead)
|
||||
if self.config.openai_api_key:
|
||||
f.write(f"OPENAI_API_KEY={self._quote_env_value(self.config.openai_api_key)}\n")
|
||||
f.write(
|
||||
f"OPENRAG_DOCUMENTS_PATHS={self._quote_env_value(self.config.openrag_documents_paths)}\n"
|
||||
)
|
||||
|
|
@ -300,6 +333,27 @@ class EnvManager:
|
|||
)
|
||||
f.write("\n")
|
||||
|
||||
# Provider API keys and endpoints (optional - can be set during onboarding)
|
||||
provider_vars = []
|
||||
if self.config.openai_api_key:
|
||||
provider_vars.append(("OPENAI_API_KEY", self.config.openai_api_key))
|
||||
if self.config.anthropic_api_key:
|
||||
provider_vars.append(("ANTHROPIC_API_KEY", self.config.anthropic_api_key))
|
||||
if self.config.ollama_endpoint:
|
||||
provider_vars.append(("OLLAMA_ENDPOINT", self.config.ollama_endpoint))
|
||||
if self.config.watsonx_api_key:
|
||||
provider_vars.append(("WATSONX_API_KEY", self.config.watsonx_api_key))
|
||||
if self.config.watsonx_endpoint:
|
||||
provider_vars.append(("WATSONX_ENDPOINT", self.config.watsonx_endpoint))
|
||||
if self.config.watsonx_project_id:
|
||||
provider_vars.append(("WATSONX_PROJECT_ID", self.config.watsonx_project_id))
|
||||
|
||||
if provider_vars:
|
||||
f.write("# AI Provider API Keys and Endpoints\n")
|
||||
for var_name, var_value in provider_vars:
|
||||
f.write(f"{var_name}={self._quote_env_value(var_value)}\n")
|
||||
f.write("\n")
|
||||
|
||||
# Ingestion settings
|
||||
f.write("# Ingestion settings\n")
|
||||
f.write(f"DISABLE_INGEST_WITH_LANGFLOW={self._quote_env_value(self.config.disable_ingest_with_langflow)}\n")
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ from rich.text import Text
|
|||
from pathlib import Path
|
||||
|
||||
from ..managers.env_manager import EnvManager
|
||||
from ..utils.validation import validate_openai_api_key, validate_documents_paths
|
||||
from ..utils.validation import (
|
||||
validate_openai_api_key,
|
||||
validate_anthropic_api_key,
|
||||
validate_ollama_endpoint,
|
||||
validate_watsonx_endpoint,
|
||||
validate_documents_paths,
|
||||
)
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
|
|
@ -37,6 +43,45 @@ class OpenAIKeyValidator(Validator):
|
|||
return self.failure("Invalid OpenAI API key format (should start with sk-)")
|
||||
|
||||
|
||||
class AnthropicKeyValidator(Validator):
|
||||
"""Validator for Anthropic API keys."""
|
||||
|
||||
def validate(self, value: str) -> ValidationResult:
|
||||
if not value:
|
||||
return self.success()
|
||||
|
||||
if validate_anthropic_api_key(value):
|
||||
return self.success()
|
||||
else:
|
||||
return self.failure("Invalid Anthropic API key format (should start with sk-ant-)")
|
||||
|
||||
|
||||
class OllamaEndpointValidator(Validator):
|
||||
"""Validator for Ollama endpoint URLs."""
|
||||
|
||||
def validate(self, value: str) -> ValidationResult:
|
||||
if not value:
|
||||
return self.success()
|
||||
|
||||
if validate_ollama_endpoint(value):
|
||||
return self.success()
|
||||
else:
|
||||
return self.failure("Invalid Ollama endpoint URL format")
|
||||
|
||||
|
||||
class WatsonxEndpointValidator(Validator):
|
||||
"""Validator for IBM watsonx.ai endpoint URLs."""
|
||||
|
||||
def validate(self, value: str) -> ValidationResult:
|
||||
if not value:
|
||||
return self.success()
|
||||
|
||||
if validate_watsonx_endpoint(value):
|
||||
return self.success()
|
||||
else:
|
||||
return self.failure("Invalid watsonx.ai endpoint URL format")
|
||||
|
||||
|
||||
class DocumentsPathValidator(Validator):
|
||||
"""Validator for documents paths."""
|
||||
|
||||
|
|
@ -227,6 +272,107 @@ class ConfigScreen(Screen):
|
|||
yield Button("Show", id="toggle-openai-key", variant="default")
|
||||
yield Static(" ")
|
||||
|
||||
# Anthropic API Key
|
||||
yield Label("Anthropic API Key")
|
||||
yield Static(
|
||||
Text("Get a key: https://console.anthropic.com/settings/keys", style="dim"),
|
||||
classes="helper-text",
|
||||
)
|
||||
yield Static(
|
||||
Text("Can also be provided during onboarding", style="dim italic"),
|
||||
classes="helper-text",
|
||||
)
|
||||
current_value = getattr(self.env_manager.config, "anthropic_api_key", "")
|
||||
with Horizontal(id="anthropic-key-row"):
|
||||
input_widget = Input(
|
||||
placeholder="sk-ant-...",
|
||||
value=current_value,
|
||||
password=True,
|
||||
validators=[AnthropicKeyValidator()],
|
||||
id="input-anthropic_api_key",
|
||||
)
|
||||
yield input_widget
|
||||
self.inputs["anthropic_api_key"] = input_widget
|
||||
yield Button("Show", id="toggle-anthropic-key", variant="default")
|
||||
yield Static(" ")
|
||||
|
||||
# Ollama Endpoint
|
||||
yield Label("Ollama Base URL")
|
||||
yield Static(
|
||||
Text("Endpoint of your Ollama server", style="dim"),
|
||||
classes="helper-text",
|
||||
)
|
||||
yield Static(
|
||||
Text("Can also be provided during onboarding", style="dim italic"),
|
||||
classes="helper-text",
|
||||
)
|
||||
current_value = getattr(self.env_manager.config, "ollama_endpoint", "")
|
||||
input_widget = Input(
|
||||
placeholder="http://localhost:11434",
|
||||
value=current_value,
|
||||
validators=[OllamaEndpointValidator()],
|
||||
id="input-ollama_endpoint",
|
||||
)
|
||||
yield input_widget
|
||||
self.inputs["ollama_endpoint"] = input_widget
|
||||
yield Static(" ")
|
||||
|
||||
# IBM watsonx.ai API Key
|
||||
yield Label("IBM watsonx.ai API Key")
|
||||
yield Static(
|
||||
Text("Get a key: https://cloud.ibm.com/iam/apikeys", style="dim"),
|
||||
classes="helper-text",
|
||||
)
|
||||
yield Static(
|
||||
Text("Can also be provided during onboarding", style="dim italic"),
|
||||
classes="helper-text",
|
||||
)
|
||||
current_value = getattr(self.env_manager.config, "watsonx_api_key", "")
|
||||
with Horizontal(id="watsonx-key-row"):
|
||||
input_widget = Input(
|
||||
placeholder="",
|
||||
value=current_value,
|
||||
password=True,
|
||||
id="input-watsonx_api_key",
|
||||
)
|
||||
yield input_widget
|
||||
self.inputs["watsonx_api_key"] = input_widget
|
||||
yield Button("Show", id="toggle-watsonx-key", variant="default")
|
||||
yield Static(" ")
|
||||
|
||||
# IBM watsonx.ai Endpoint
|
||||
yield Label("IBM watsonx.ai Endpoint")
|
||||
yield Static(
|
||||
Text("Example: https://us-south.ml.cloud.ibm.com", style="dim"),
|
||||
classes="helper-text",
|
||||
)
|
||||
current_value = getattr(self.env_manager.config, "watsonx_endpoint", "")
|
||||
input_widget = Input(
|
||||
placeholder="https://us-south.ml.cloud.ibm.com",
|
||||
value=current_value,
|
||||
validators=[WatsonxEndpointValidator()],
|
||||
id="input-watsonx_endpoint",
|
||||
)
|
||||
yield input_widget
|
||||
self.inputs["watsonx_endpoint"] = input_widget
|
||||
yield Static(" ")
|
||||
|
||||
# IBM watsonx.ai Project ID
|
||||
yield Label("IBM watsonx.ai Project ID")
|
||||
yield Static(
|
||||
Text("Find in your IBM Cloud project settings", style="dim"),
|
||||
classes="helper-text",
|
||||
)
|
||||
current_value = getattr(self.env_manager.config, "watsonx_project_id", "")
|
||||
input_widget = Input(
|
||||
placeholder="",
|
||||
value=current_value,
|
||||
id="input-watsonx_project_id",
|
||||
)
|
||||
yield input_widget
|
||||
self.inputs["watsonx_project_id"] = input_widget
|
||||
yield Static(" ")
|
||||
|
||||
# Add OAuth fields only in full mode
|
||||
if self.mode == "full":
|
||||
# Google OAuth Client ID
|
||||
|
|
@ -550,6 +696,18 @@ class ConfigScreen(Screen):
|
|||
if input_widget:
|
||||
input_widget.password = not input_widget.password
|
||||
event.button.label = "🙈" if not input_widget.password else "👁"
|
||||
elif event.button.id == "toggle-anthropic-key":
|
||||
# Toggle Anthropic API key visibility
|
||||
input_widget = self.inputs.get("anthropic_api_key")
|
||||
if input_widget:
|
||||
input_widget.password = not input_widget.password
|
||||
event.button.label = "Hide" if not input_widget.password else "Show"
|
||||
elif event.button.id == "toggle-watsonx-key":
|
||||
# Toggle watsonx API key visibility
|
||||
input_widget = self.inputs.get("watsonx_api_key")
|
||||
if input_widget:
|
||||
input_widget.password = not input_widget.password
|
||||
event.button.label = "Hide" if not input_widget.password else "Show"
|
||||
|
||||
def action_generate(self) -> None:
|
||||
"""Generate secure passwords for admin accounts."""
|
||||
|
|
|
|||
|
|
@ -63,6 +63,27 @@ def validate_openai_api_key(key: str) -> bool:
|
|||
return key.startswith("sk-") and len(key) > 20
|
||||
|
||||
|
||||
def validate_anthropic_api_key(key: str) -> bool:
|
||||
"""Validate Anthropic API key format."""
|
||||
if not key:
|
||||
return False
|
||||
return key.startswith("sk-ant-") and len(key) > 20
|
||||
|
||||
|
||||
def validate_ollama_endpoint(endpoint: str) -> bool:
|
||||
"""Validate Ollama endpoint URL format."""
|
||||
if not endpoint:
|
||||
return False
|
||||
return validate_url(endpoint)
|
||||
|
||||
|
||||
def validate_watsonx_endpoint(endpoint: str) -> bool:
|
||||
"""Validate IBM watsonx.ai endpoint URL format."""
|
||||
if not endpoint:
|
||||
return False
|
||||
return validate_url(endpoint)
|
||||
|
||||
|
||||
def validate_google_oauth_client_id(client_id: str) -> bool:
|
||||
"""Validate Google OAuth client ID format."""
|
||||
if not client_id:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue