Skip to content

feat(wren-ai-service): create Streamlit UI for configuring LLM models #1690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 75 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
da42d8d
fix: add fmt.Scanln for debugging missing error report
yichieh-lu Apr 10, 2025
4584b05
Merge branch 'main' into main
cyyeh Apr 10, 2025
462554d
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 10, 2025
96667ab
Merge branch 'main' of https://github.com/yichieh-lu/WrenAI
yichieh-lu Apr 10, 2025
a275e42
Remove redundant code
yichieh-lu Apr 11, 2025
c96f296
Add initial config.grok.yaml to support deploymen
yichieh-lu Apr 12, 2025
e7edbd3
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 14, 2025
398a14c
Remove redundant engine declaration
yichieh-lu Apr 14, 2025
e090cf0
Remove incorrect settings from config_examples YAML files
yichieh-lu Apr 14, 2025
a39d71f
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 16, 2025
d59c6bf
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 21, 2025
1168301
Draft streamlit_ui
yichieh-lu Apr 21, 2025
c70f183
Build a testing streamlit_ui
yichieh-lu Apr 23, 2025
a261c3b
Extract constants to constants.py
yichieh-lu Apr 23, 2025
0cf0ddd
refactor: extract download_config and load_block from custom_llm_ui.py
yichieh-lu Apr 23, 2025
35a9743
refactor: split load_blocks into load_yaml_list and group_blocks
yichieh-lu Apr 23, 2025
66488b1
refactor: move session state handling to ConfigState class in session…
yichieh-lu Apr 23, 2025
54885d7
style: remove trailing whitespace
yichieh-lu Apr 23, 2025
c6bc142
refactor: extract UI layout and elements to ui_components.py
yichieh-lu Apr 23, 2025
01d3208
Refactor app structure and fix data type issues
yichieh-lu Apr 24, 2025
04edc56
feat: check for duplicate alias names
yichieh-lu Apr 24, 2025
fdb2114
refactor: extract preview and generate YAML UI to ui_components.py
yichieh-lu Apr 24, 2025
7f8daca
feat: add pipeline configuration in ui_components.py
yichieh-lu Apr 24, 2025
494fac4
feat: add dry_run_test.py testing api_key and embedding model with li…
yichieh-lu Apr 25, 2025
181eb6f
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 26, 2025
7436303
feat: add validate function ensuring every field can be validated
yichieh-lu Apr 26, 2025
0f8e386
feat: support multiple API keys configuration for different LLMs
yichieh-lu Apr 27, 2025
cf14ae5
feat: support multiple API keys configuration for different LLMs
yichieh-lu Apr 27, 2025
ac81e37
feat: support saving multiple API keys and enable validation for LLM …
yichieh-lu Apr 29, 2025
9d4f0a0
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 29, 2025
90dd5a3
fix: ensure config.yaml is downloaded correctly
yichieh-lu Apr 29, 2025
a608d80
fix: ensure config.yaml is downloaded correctly
yichieh-lu Apr 29, 2025
667043b
feat: add api_base support to render_embedder and session state
yichieh-lu Apr 29, 2025
5a675aa
feat: get the latest WrenAI version in constants.py
yichieh-lu Apr 29, 2025
57077c2
feat: add selectbox for users to choose config.example.yaml by LLM pr…
yichieh-lu Apr 30, 2025
54f133d
chore: improve code comments and docstrings across UI components
yichieh-lu Apr 30, 2025
cad7459
chore: add initial requirements.txt with ui dependencies
yichieh-lu Apr 30, 2025
e9639e4
Merge remote-tracking branch 'upstream/main'
yichieh-lu Apr 30, 2025
05aac3d
chore: updates requirements.txt with ui dependencies
yichieh-lu May 1, 2025
f1b3e45
chore: improve code comments and docstrings across UI components
yichieh-lu May 1, 2025
6533430
feat: add Finished.setting to close Streamlit UI, continue CLI setup,…
yichieh-lu May 2, 2025
366db9f
feat: add Streamlit UI Dockerfile and implement 'custom' launch option
yichieh-lu May 2, 2025
9ce5946
refactor: decouple RunStreamlitUIContainer logic from launch.go and d…
yichieh-lu May 2, 2025
6bed2b4
feat: add 'custom' mode to launch Streamlit UI and ensure config file…
yichieh-lu May 5, 2025
6a068d1
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 5, 2025
d40ae29
chore: rename streamlit-ui to providers-setup and move to tools direc…
yichieh-lu May 6, 2025
011ce4b
feat: rewrite Dockerfile to use Poetry and remove requirements.txt
yichieh-lu May 6, 2025
bb8dab6
chore: rename streamlit-ui to providers-setup
yichieh-lu May 6, 2025
b4d5b4e
update dependencies
cyyeh May 6, 2025
6255290
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 6, 2025
d0d0360
Merge branches 'main' and 'main' of https://github.com/yichieh-lu/WrenAI
yichieh-lu May 6, 2025
8341378
chore: fix Streamlit UI layout
yichieh-lu May 6, 2025
eeedb81
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 7, 2025
d9bd650
Merge branch 'main' into main
cyyeh May 9, 2025
9f830b1
Merge branch 'main' into main
cyyeh May 9, 2025
430c67b
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 13, 2025
fc5ee05
refactor(wren-ai-service): streamline UI components and enhance confi…
yichieh-lu May 13, 2025
efc6529
refactor(wren-ai-service): improve configuration path handling and UI…
yichieh-lu May 13, 2025
095475c
feat(wren-ai-service): add validation for configuration blocks and im…
yichieh-lu May 13, 2025
8f892e6
refactor(wren-ai-service): enhance config extraction and improve UI c…
yichieh-lu May 13, 2025
2dc1b38
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 22, 2025
ea0765c
feat(workflow): add setup provider image workflow
yichieh-lu May 22, 2025
06ccc36
fix(workflow): update IMAGE_NAME to use repository owner
yichieh-lu May 22, 2025
1b80ae1
fix(workflow): update Docker image tags to include registry
yichieh-lu May 22, 2025
1cbeb60
fix(workflow): comment out ARM64 runner configuration in build matrix
yichieh-lu May 22, 2025
de9d460
fix(workflow): enable ARM64 runner configuration in build matrix
yichieh-lu May 22, 2025
f08abae
fix(workflow): enhance setup-provider-image workflow with multi-archi…
yichieh-lu May 22, 2025
b983e36
Merge remote-tracking branch 'upstream/main'
yichieh-lu May 26, 2025
bdd4f9f
feat(pipeline): enhance pipeline initialization to fetch and add miss…
yichieh-lu May 26, 2025
0d236be
feat(ui): add context window size configuration to LLM setup and vali…
yichieh-lu May 28, 2025
a706d3b
Merge branch 'main' into feature/providers-setup
yichieh-lu Jun 5, 2025
8afc269
fix: update local path for .env file and improve error handling in em…
yichieh-lu Jun 5, 2025
0b991c6
feat: enhance Streamlit UI container setup with configurable port and…
yichieh-lu Jun 5, 2025
d43e7cf
Update wren-ai-service/tools/providers-setup/ui_components.py
yichieh-lu Jun 5, 2025
2c4614b
Merge branch 'main' into feature/providers-setup
cyyeh Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add selectbox for users to choose config.example.yaml by LLM pr…
…ovider
  • Loading branch information
yichieh-lu committed Apr 30, 2025
commit 57077c22140645f46287d70469420dae01467e93
12 changes: 3 additions & 9 deletions wren-ai-service/streamlit-ui/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import constants as cst
from config_loader import download_config, load_yaml_list, group_blocks
from config_loader import load_config_yaml_blocks, group_blocks
from session_state import ConfigState
from ui_components import (
render_llm_config,
Expand All @@ -18,13 +17,7 @@
initial_sidebar_state="expanded" # 控制側邊欄的初始狀態
)

if not cst.CONFIG_IN_PATH.exists():
download_state, download_msg = download_config()
if not download_state:
st.error(download_msg)


yaml_list = load_yaml_list(cst.CONFIG_IN_PATH)
yaml_list = load_config_yaml_blocks()
blocks = group_blocks(yaml_list)

llm_block = blocks.get("llm", {})
Expand All @@ -43,6 +36,7 @@
ConfigState.init(llm_block, embedder_block, document_store_block, pipeline_block)

with col1:
st.subheader("LLM Configuration")
# =====================
# LLM Configuration UI
# =====================
Expand Down
82 changes: 75 additions & 7 deletions wren-ai-service/streamlit-ui/config_loader.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,65 @@
# config_loader.py
import requests
import yaml
from session_state import ConfigState
from pathlib import Path
import constants as cst
from typing import Any, Dict, List

def download_config():
def load_config_yaml_blocks() -> List[Dict[str, Any]]:
"""
嘗試從本地讀 config.yaml,若不存在則從 GitHub 讀取(不下載)。
"""
if cst.CONFIG_IN_PATH.exists():
try:
return load_yaml_list(cst.CONFIG_IN_PATH)
except Exception as e:
print(f"❌ Failed to parse local config.yaml: {e}")
return []
else:
return fetch_yaml_from_url(cst.CONFIG_URL)

def load_selected_example_yaml(example_name: str) -> List[Dict[str, Any]]:
url = cst.CONFIG_EXAMPLES_SELECTED_URL + example_name
return fetch_yaml_from_url(url)

def fetch_yaml_from_url(url: str) -> List[Dict[str, Any]]:
try:
response = requests.get(cst.CONFIG_URL, timeout=cst.REQUEST_TIMEOUT)
response = requests.get(url, timeout=cst.REQUEST_TIMEOUT)
response.raise_for_status()
cst.CONFIG_IN_PATH.write_text(response.text, encoding='utf-8')
return True, "Download Successed"
except requests.RequestException as e:
return False, str(e)
config_list = list(yaml.safe_load_all(response.text))

if not config_list:
raise ValueError(f"⚠️ GitHub 回傳的 YAML 是空的,URL: {url}")

return config_list

except (requests.RequestException, ValueError, yaml.YAMLError) as e:
print(f"❌ Error loading config from {url}: {e}")
return []

def extract_config_blocks(config_list: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
傳入 YAML List,回傳各 block 分類後的 dict
"""
grouped = group_blocks(config_list)
return {
"llm": grouped.get("llm", {}),
"embedder": grouped.get("embedder", {}),
"document_store": grouped.get("document_store", {}),
"pipeline": grouped.get("pipeline", {})
}

def load__selected_config_yaml_blocks(selected_examples) -> List[Dict[str, Any]]:
selected_url = cst.CONFIG_EXAMPLES_SELECTED_URL + selected_examples
# return selected_url
try:
response = requests.get(selected_url, timeout=cst.REQUEST_TIMEOUT)
response.raise_for_status()
return list(yaml.safe_load_all(response.text))
except requests.RequestException as e:
print(f"❌ Error loading config from GitHub: {e}")
return []

def load_yaml_list(path: Path) -> List[Dict[str, Any]]:
with path.open("r", encoding="utf-8") as f:
return list(yaml.safe_load_all(f))
Expand All @@ -33,3 +79,25 @@ def group_blocks(blocks: List[Dict[str, Any]]) -> Dict[str, Any]:
save_blocks[key] = block
return save_blocks

def fetch_example_yaml_filenames() -> List[str]:
"""從 GitHub 的 config_examples 目錄中取得所有 .yaml 檔案名稱(不載入內容)"""
try:
response = requests.get(cst.CONFIG_EXAMPLES_URL, timeout=cst.REQUEST_TIMEOUT)
response.raise_for_status()
file_list = response.json()
return [f["name"] for f in file_list if f["name"].endswith(".yaml")]
except requests.RequestException as e:
print(f"Error fetching config example filenames: {e}")
return []

def apply_config_blocks(config_blocks: List[Dict[str, Any]]):
grouped = extract_config_blocks(config_blocks)

# 更新 ConfigState
ConfigState.init(
grouped["llm"],
grouped["embedder"],
grouped["document_store"],
grouped["pipeline"],
force=True
)
2 changes: 2 additions & 0 deletions wren-ai-service/streamlit-ui/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def get_latest_config_version():
# --- constant ---
CONFIG_VERSION = get_latest_config_version()
CONFIG_URL = f"https://raw.githubusercontent.com/Canner/WrenAI/{CONFIG_VERSION}/docker/config.example.yaml"
CONFIG_EXAMPLES_URL = f"https://api.github.com/repos/Canner/WrenAI/contents/wren-ai-service/docs/config_examples?ref={CONFIG_VERSION}"
CONFIG_EXAMPLES_SELECTED_URL = f"https://raw.githubusercontent.com/Canner/WrenAI/{CONFIG_VERSION}/wren-ai-service/docs/config_examples/"
CONFIG_IN_PATH = Path("config.yaml")
CONFIG_OUT_PATH = Path("generated_config.yaml")
REQUEST_TIMEOUT = 10 # seconds
8 changes: 7 additions & 1 deletion wren-ai-service/streamlit-ui/session_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ConfigState:
PIPELINE_KEY = "pipeline"
API_KEY = "api_key"
API_KEY_FORM = "api_key_form"
EXAMPLE_CONFIG_NAMES_KEY = "example_yaml_names"

@classmethod
def init(cls, llm_block, embedder_block, document_store_block, pipeline_block,force=False):
Expand All @@ -19,6 +20,7 @@ def init(cls, llm_block, embedder_block, document_store_block, pipeline_block,fo
cls.init_document_store(document_store_block, force=force)
cls.init_pipeline(pipeline_block, force=force)
cls.init_apikey()
cls.init_example_configs()

@classmethod
def init_llm_forms(cls, llm_block, force=False):
Expand Down Expand Up @@ -103,11 +105,15 @@ def init_pipeline(cls, pipeline_block, force=False):
}

@classmethod
def init_apikey(cls):
def init_apikey(cls, force=False):
if cls.API_KEY not in st.session_state:
st.session_state[cls.API_KEY] = {}
if ConfigState.API_KEY_FORM not in st.session_state:
st.session_state[cls.API_KEY_FORM] = []

@classmethod
def init_example_configs(cls, force=False):
from config_loader import fetch_example_yaml_filenames # 避免循環 import,要晚點 import
st.session_state[cls.EXAMPLE_CONFIG_NAMES_KEY] = fetch_example_yaml_filenames()
# 可以額外做 CRUD:新增 LLM、刪除 LLM、更新 Embedder...

56 changes: 24 additions & 32 deletions wren-ai-service/streamlit-ui/ui_components.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import streamlit as st
import uuid
from session_state import ConfigState
from config_loader import group_blocks
from config_loader import load_selected_example_yaml, apply_config_blocks
from dry_run_test import llm_completion_test, llm_embedding_test
import yaml
import os

def render_import_yaml():
with st.expander("Import Configuration YAML", expanded=False):
# IMPORT LATESET VERSION OF CONFIG.EXAMPLES.YAML FORM GITHUB
example_names = ["None"] + st.session_state.get("example_yaml_names", [])
selected_examples_yaml = st.selectbox("Select config.examples.yaml from WrenAI", options=example_names)

if selected_examples_yaml != "None" and st.button("Import.yaml", key="import__examples_yaml"):
blocks = load_selected_example_yaml(selected_examples_yaml)
apply_config_blocks(blocks)
st.success("YAML 匯入成功,設定已更新。")

uploaded_file = st.file_uploader("Choose your own YAML file", type=["yaml", "yml"])
if uploaded_file is not None and st.button("Import.yaml", key="import_own_yaml"):
try:
blocks = list(yaml.safe_load_all(uploaded_file))
apply_config_blocks(blocks)
except Exception as e:
st.error(f"匯入 YAML 檔案時發生錯誤: {e}")



def render_apikey():
with st.expander("API Key", expanded=False):

Expand All @@ -20,9 +41,9 @@ def render_apikey():

with kcol:
if apikey.get("is_saved"):
st.text_input("apikey_service", key=f"api_key_{apikey['id']}", value=apikey["key"], disabled=True)
st.text_input("apikey_service (LLMAI_API_KEY)", key=f"api_key_{apikey['id']}", value=apikey["key"], disabled=True)
else:
apikey["key"] = st.text_input("apikey_service", key=f"api_key_{apikey['id']}", value=apikey["key"])
apikey["key"] = st.text_input("apikey_service (LLMAI_API_KEY)", key=f"api_key_{apikey['id']}", value=apikey["key"])

with vcol:
if apikey.get("is_saved"):
Expand Down Expand Up @@ -65,35 +86,6 @@ def render_apikey():
item["is_saved"] = True
st.rerun()

def render_import_yaml():
st.subheader("LLM Configuration")
# IMPORT YAML
uploaded_file = st.file_uploader("Choose a YAML file", type=["yaml", "yml"])

if uploaded_file is not None:
if st.button("Import.yaml", key="import_yaml"):
try:
# 解析使用者上傳的 yaml 檔案
user_config_list = list(yaml.safe_load_all(uploaded_file))
user_config_block = group_blocks(user_config_list) # 將 YAML 轉換為字典格式

# 用於更新的暫存區
user_llm_block = user_config_block.get("llm", {})
user_embedder_block = user_config_block.get("embedder", {})
user_document_store_block = user_config_block.get("document_store", {})
user_pipeline_block = user_config_block.get("pipeline", {})

# 僅在有新資料時才更新對應的 block
if user_llm_block: llm_block = user_llm_block
if user_embedder_block: embedder_block = user_embedder_block
if user_document_store_block: document_store_block = user_document_store_block
if user_pipeline_block: pipeline_block = user_pipeline_block

ConfigState.init(llm_block, embedder_block, document_store_block, pipeline_block,force=True) # 強制重新初始化 Session State
st.success("YAML 匯入成功,設定已更新。")
except Exception as e:
st.error(f"匯入 YAML 檔案時發生錯誤: {e}")

def render_llm_config():

# 用一個 dict 來暫存每個 form_id 對應的 title
Expand Down
Loading