diff --git a/apps/ai-pipeline-studio-app/app.py b/apps/ai-pipeline-studio-app/app.py index 579e968..9944fa0 100644 --- a/apps/ai-pipeline-studio-app/app.py +++ b/apps/ai-pipeline-studio-app/app.py @@ -4453,10 +4453,24 @@ def _fmt_project_dir(dir_name: str) -> str: ) openai_key = (openai_key_input or "").strip() st.session_state["OPENAI_API_KEY"] = openai_key + default_openai_base_url = st.session_state.get("OPENAI_BASE_URL") + if default_openai_base_url is None: + default_openai_base_url = os.environ.get("OPENAI_BASE_URL", "") + openai_base_url_input = st.text_input( + "OpenAI base URL (optional)", + value=default_openai_base_url, + key="openai_base_url_input", + help="Override OpenAI base URL, e.g. http://localhost:8000/v1", + ) + openai_base_url = (openai_base_url_input or "").strip() + st.session_state["OPENAI_BASE_URL"] = openai_base_url if openai_key: try: - _ = OpenAI(api_key=openai_key).models.list() + client_kwargs = {"api_key": openai_key} + if openai_base_url: + client_kwargs["base_url"] = openai_base_url + _ = OpenAI(**client_kwargs).models.list() key_status = "ok" st.success("API Key is valid!") except Exception as e: @@ -4886,6 +4900,9 @@ def _fmt_dataset(did: str) -> str: # LLM credentials are only required when running chat (Pipeline Studio + previews should still work). llm_provider_selected = st.session_state.get("llm_provider") or "OpenAI" resolved_api_key = (st.session_state.get("OPENAI_API_KEY") or "").strip() or None +resolved_openai_base_url = ( + (st.session_state.get("OPENAI_BASE_URL") or "").strip() or None +) resolved_ollama_model = (st.session_state.get("ollama_model") or "").strip() or None @@ -4893,6 +4910,7 @@ def build_team( llm_provider: str, model_name: str, openai_api_key: str | None, + openai_base_url: str | None, ollama_base_url: str | None, use_memory: bool, sql_url: str, @@ -4935,6 +4953,15 @@ def _openai_requires_responses(model: str | None) -> bool: "model": model_name, "api_key": openai_api_key, } + if openai_base_url: + try: + sig = inspect.signature(ChatOpenAI) + if "base_url" in sig.parameters: + llm_kwargs["base_url"] = openai_base_url + elif "openai_api_base" in sig.parameters: + llm_kwargs["openai_api_base"] = openai_base_url + except Exception: + llm_kwargs["base_url"] = openai_base_url if _openai_requires_responses(model_name): llm_kwargs["use_responses_api"] = True llm_kwargs["output_version"] = "responses/v1" @@ -5952,6 +5979,7 @@ def _apply_chat_dataset_selection() -> None: llm_provider_selected, model_choice, resolved_api_key if llm_provider_selected == "OpenAI" else None, + resolved_openai_base_url if llm_provider_selected == "OpenAI" else None, st.session_state.get("ollama_base_url"), add_memory, st.session_state.get("sql_url", DEFAULT_SQL_URL), diff --git a/apps/exploratory-copilot-app/app.py b/apps/exploratory-copilot-app/app.py index cf96e67..8fe5181 100644 --- a/apps/exploratory-copilot-app/app.py +++ b/apps/exploratory-copilot-app/app.py @@ -8,6 +8,8 @@ # !pip install git+https://github.com/business-science/ai-data-science-team.git --upgrade from openai import OpenAI +import inspect +import os import streamlit as st import streamlit.components.v1 as components import pandas as pd @@ -186,9 +188,21 @@ def render_report_iframe( type="password", help="Your OpenAI API key is required for the app to function.", ) +default_openai_base_url = st.session_state.get("OPENAI_BASE_URL") +if default_openai_base_url is None: + default_openai_base_url = os.environ.get("OPENAI_BASE_URL", "") +st.session_state["OPENAI_BASE_URL"] = st.sidebar.text_input( + "Base URL (optional)", + value=default_openai_base_url, + help="Override OpenAI base URL, e.g. http://localhost:8000/v1", +) +openai_base_url = (st.session_state.get("OPENAI_BASE_URL") or "").strip() or None if st.session_state["OPENAI_API_KEY"]: - client = OpenAI(api_key=st.session_state["OPENAI_API_KEY"]) + client_kwargs = {"api_key": st.session_state["OPENAI_API_KEY"]} + if openai_base_url: + client_kwargs["base_url"] = openai_base_url + client = OpenAI(**client_kwargs) try: models = client.models.list() st.success("API Key is valid!") @@ -199,7 +213,25 @@ def render_report_iframe( st.stop() model_option = st.sidebar.selectbox("Choose OpenAI model", MODEL_LIST, index=0) -OPENAI_LLM = ChatOpenAI(model=model_option, api_key=st.session_state["OPENAI_API_KEY"]) +def _chat_openai_kwargs(model_name, api_key, base_url): + kwargs = {"model": model_name, "api_key": api_key} + if base_url: + try: + sig = inspect.signature(ChatOpenAI) + if "base_url" in sig.parameters: + kwargs["base_url"] = base_url + elif "openai_api_base" in sig.parameters: + kwargs["openai_api_base"] = base_url + except Exception: + kwargs["base_url"] = base_url + return kwargs + + +OPENAI_LLM = ChatOpenAI( + **_chat_openai_kwargs( + model_option, st.session_state["OPENAI_API_KEY"], openai_base_url + ) +) llm = OPENAI_LLM # ============================================================================= diff --git a/apps/pandas-data-analyst-app/app.py b/apps/pandas-data-analyst-app/app.py index cef9a77..c6152eb 100644 --- a/apps/pandas-data-analyst-app/app.py +++ b/apps/pandas-data-analyst-app/app.py @@ -9,6 +9,8 @@ from openai import OpenAI +import inspect +import os import streamlit as st import pandas as pd import plotly.io as pio @@ -67,11 +69,23 @@ type="password", help="Your OpenAI API key is required for the app to function.", ) +default_openai_base_url = st.session_state.get("OPENAI_BASE_URL") +if default_openai_base_url is None: + default_openai_base_url = os.environ.get("OPENAI_BASE_URL", "") +st.session_state["OPENAI_BASE_URL"] = st.sidebar.text_input( + "Base URL (optional)", + value=default_openai_base_url, + help="Override OpenAI base URL, e.g. http://localhost:8000/v1", +) # Test OpenAI API Key +openai_base_url = (st.session_state.get("OPENAI_BASE_URL") or "").strip() or None if st.session_state["OPENAI_API_KEY"]: # Set the API key for OpenAI - client = OpenAI(api_key=st.session_state["OPENAI_API_KEY"]) + client_kwargs = {"api_key": st.session_state["OPENAI_API_KEY"]} + if openai_base_url: + client_kwargs["base_url"] = openai_base_url + client = OpenAI(**client_kwargs) # Test the API key (optional) try: @@ -89,7 +103,25 @@ model_option = st.sidebar.selectbox("Choose OpenAI model", MODEL_LIST, index=0) -llm = ChatOpenAI(model=model_option, api_key=st.session_state["OPENAI_API_KEY"]) +def _chat_openai_kwargs(model_name, api_key, base_url): + kwargs = {"model": model_name, "api_key": api_key} + if base_url: + try: + sig = inspect.signature(ChatOpenAI) + if "base_url" in sig.parameters: + kwargs["base_url"] = base_url + elif "openai_api_base" in sig.parameters: + kwargs["openai_api_base"] = base_url + except Exception: + kwargs["base_url"] = base_url + return kwargs + + +llm = ChatOpenAI( + **_chat_openai_kwargs( + model_option, st.session_state["OPENAI_API_KEY"], openai_base_url + ) +) # --------------------------- diff --git a/apps/sql-database-agent-app/app.py b/apps/sql-database-agent-app/app.py index 8bd2d69..f08be13 100644 --- a/apps/sql-database-agent-app/app.py +++ b/apps/sql-database-agent-app/app.py @@ -9,6 +9,8 @@ from openai import OpenAI +import inspect +import os import streamlit as st import sqlalchemy as sql import pandas as pd @@ -69,11 +71,23 @@ st.sidebar.header("Enter your OpenAI API Key") st.session_state["OPENAI_API_KEY"] = st.sidebar.text_input("API Key", type="password", help="Your OpenAI API key is required for the app to function.") +default_openai_base_url = st.session_state.get("OPENAI_BASE_URL") +if default_openai_base_url is None: + default_openai_base_url = os.environ.get("OPENAI_BASE_URL", "") +st.session_state["OPENAI_BASE_URL"] = st.sidebar.text_input( + "Base URL (optional)", + value=default_openai_base_url, + help="Override OpenAI base URL, e.g. http://localhost:8000/v1", +) # Test OpenAI API Key +openai_base_url = (st.session_state.get("OPENAI_BASE_URL") or "").strip() or None if st.session_state["OPENAI_API_KEY"]: # Set the API key for OpenAI - client = OpenAI(api_key=st.session_state["OPENAI_API_KEY"]) + client_kwargs = {"api_key": st.session_state["OPENAI_API_KEY"]} + if openai_base_url: + client_kwargs["base_url"] = openai_base_url + client = OpenAI(**client_kwargs) # Test the API key (optional) try: @@ -95,9 +109,24 @@ index=0 ) +def _chat_openai_kwargs(model_name, api_key, base_url): + kwargs = {"model": model_name, "api_key": api_key} + if base_url: + try: + sig = inspect.signature(ChatOpenAI) + if "base_url" in sig.parameters: + kwargs["base_url"] = base_url + elif "openai_api_base" in sig.parameters: + kwargs["openai_api_base"] = base_url + except Exception: + kwargs["base_url"] = base_url + return kwargs + + OPENAI_LLM = ChatOpenAI( - model = model_option, - api_key=st.session_state["OPENAI_API_KEY"] + **_chat_openai_kwargs( + model_option, st.session_state["OPENAI_API_KEY"], openai_base_url + ) ) llm = OPENAI_LLM