make openai optional in tui and lazy client creation in backend
This commit is contained in:
parent
091481f4c7
commit
80fdd9680d
5 changed files with 65 additions and 10 deletions
|
|
@ -279,7 +279,7 @@ class AppClients:
|
|||
self.opensearch = None
|
||||
self.langflow_client = None
|
||||
self.langflow_http_client = None
|
||||
self.patched_async_client = None
|
||||
self._patched_async_client = None # Private attribute
|
||||
self.converter = None
|
||||
|
||||
async def initialize(self):
|
||||
|
|
@ -318,8 +318,21 @@ class AppClients:
|
|||
"No Langflow client initialized yet, will attempt later on first use"
|
||||
)
|
||||
|
||||
# Initialize patched OpenAI client
|
||||
self.patched_async_client = patch_openai_with_mcp(AsyncOpenAI())
|
||||
# Initialize patched OpenAI client if API key is available
|
||||
# This allows the app to start even if OPENAI_API_KEY is not set yet
|
||||
# (e.g., when it will be provided during onboarding)
|
||||
# The property will handle lazy initialization if needed later
|
||||
if self._patched_async_client is None:
|
||||
try:
|
||||
openai_key = os.getenv("OPENAI_API_KEY")
|
||||
if openai_key:
|
||||
self._patched_async_client = patch_openai_with_mcp(AsyncOpenAI())
|
||||
logger.info("OpenAI client initialized with API key from environment")
|
||||
else:
|
||||
logger.info("OpenAI API key not found in environment - will be initialized on first use if needed")
|
||||
except Exception as e:
|
||||
logger.warning("Failed to initialize OpenAI client", error=str(e))
|
||||
self._patched_async_client = None
|
||||
|
||||
# Initialize document converter
|
||||
self.converter = create_document_converter(ocr_engine=DOCLING_OCR_ENGINE)
|
||||
|
|
@ -350,6 +363,41 @@ class AppClients:
|
|||
self.langflow_client = None
|
||||
return self.langflow_client
|
||||
|
||||
@property
|
||||
def patched_async_client(self):
|
||||
"""
|
||||
Property that ensures OpenAI client is initialized on first access.
|
||||
This allows lazy initialization so the app can start without an API key.
|
||||
"""
|
||||
if self._patched_async_client is not None:
|
||||
return self._patched_async_client
|
||||
|
||||
# Try to initialize the client on-demand
|
||||
# First check if OPENAI_API_KEY is in environment
|
||||
openai_key = os.getenv("OPENAI_API_KEY")
|
||||
|
||||
if not openai_key:
|
||||
# Try to get from config (in case it was set during onboarding)
|
||||
try:
|
||||
config = get_openrag_config()
|
||||
if config and config.provider and config.provider.api_key:
|
||||
openai_key = config.provider.api_key
|
||||
# Set it in environment so AsyncOpenAI can pick it up
|
||||
os.environ["OPENAI_API_KEY"] = openai_key
|
||||
logger.info("Loaded OpenAI API key from config file")
|
||||
except Exception as e:
|
||||
logger.debug("Could not load OpenAI key from config", error=str(e))
|
||||
|
||||
# Try to initialize the client - AsyncOpenAI() will read from environment
|
||||
try:
|
||||
self._patched_async_client = patch_openai_with_mcp(AsyncOpenAI())
|
||||
logger.info("OpenAI client initialized on-demand")
|
||||
except Exception as e:
|
||||
logger.error("Failed to initialize OpenAI client on-demand", error=str(e))
|
||||
raise ValueError(f"Failed to initialize OpenAI client: {str(e)}. Please complete onboarding or set OPENAI_API_KEY environment variable.")
|
||||
|
||||
return self._patched_async_client
|
||||
|
||||
async def langflow_request(self, method: str, endpoint: str, **kwargs):
|
||||
"""Central method for all Langflow API requests"""
|
||||
api_key = await generate_langflow_api_key()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class ConnectorService:
|
|||
task_service=None,
|
||||
session_manager=None,
|
||||
):
|
||||
self.openai_client = patched_async_client
|
||||
self.clients = patched_async_client # Store the clients object to access the property
|
||||
self.process_pool = process_pool
|
||||
self.embed_model = embed_model
|
||||
self.index_name = index_name
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ async def initialize_services():
|
|||
session_manager=session_manager,
|
||||
)
|
||||
openrag_connector_service = ConnectorService(
|
||||
patched_async_client=clients.patched_async_client,
|
||||
patched_async_client=clients, # Pass the clients object itself
|
||||
process_pool=process_pool,
|
||||
embed_model=get_embedding_model(),
|
||||
index_name=INDEX_NAME,
|
||||
|
|
|
|||
|
|
@ -170,8 +170,9 @@ class EnvManager:
|
|||
"""
|
||||
self.config.validation_errors.clear()
|
||||
|
||||
# Always validate OpenAI API key
|
||||
if not validate_openai_api_key(self.config.openai_api_key):
|
||||
# OpenAI API key is now optional (can be provided during onboarding)
|
||||
# Only validate format if a key is provided
|
||||
if self.config.openai_api_key and not validate_openai_api_key(self.config.openai_api_key):
|
||||
self.config.validation_errors["openai_api_key"] = (
|
||||
"Invalid OpenAI API key format (should start with sk-)"
|
||||
)
|
||||
|
|
@ -268,7 +269,9 @@ 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")
|
||||
f.write(f"OPENAI_API_KEY={self._quote_env_value(self.config.openai_api_key)}\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"
|
||||
)
|
||||
|
|
@ -345,7 +348,7 @@ class EnvManager:
|
|||
def get_no_auth_setup_fields(self) -> List[tuple[str, str, str, bool]]:
|
||||
"""Get fields required for no-auth setup mode. Returns (field_name, display_name, placeholder, can_generate)."""
|
||||
return [
|
||||
("openai_api_key", "OpenAI API Key", "sk-...", False),
|
||||
("openai_api_key", "OpenAI API Key", "sk-... or leave empty", False),
|
||||
(
|
||||
"opensearch_password",
|
||||
"OpenSearch Password",
|
||||
|
|
|
|||
|
|
@ -203,12 +203,16 @@ class ConfigScreen(Screen):
|
|||
yield Static(" ")
|
||||
|
||||
# OpenAI API Key
|
||||
yield Label("OpenAI API Key *")
|
||||
yield Label("OpenAI API Key")
|
||||
# Where to create OpenAI keys (helper above the box)
|
||||
yield Static(
|
||||
Text("Get a key: https://platform.openai.com/api-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, "openai_api_key", "")
|
||||
with Horizontal(id="openai-key-row"):
|
||||
input_widget = Input(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue