| """dashboard/model_picker.py β sidebar provider/model/API-key picker. |
| |
| Renders a compact popover in the sidebar showing the active provider+model, |
| and lets the visitor paste their own API key for this browser session only. |
| Returns a frozen ``RunConfig`` (or None when no key is available) that the |
| caller threads through to generation / chat / translation β never re-read |
| from session_state after the click that consumes it. |
| """ |
| from __future__ import annotations |
|
|
| import streamlit as st |
|
|
| from agent.llm import ( |
| MODEL_CATALOG, |
| PROVIDER_LABELS, |
| RunConfig, |
| console_url, |
| key_looks_valid, |
| resolve_api_key, |
| ) |
| from dashboard.i18n import t |
|
|
|
|
| def render() -> tuple[RunConfig | None, str]: |
| """Render the Model popover. Returns (config, key_source). |
| |
| config is None when no key is available anywhere (env or pasted) β |
| callers must block the action (Generate / chat) in that case. |
| """ |
| st.session_state.setdefault("llm_provider", "anthropic") |
| provider = st.session_state["llm_provider"] |
|
|
| default_model = MODEL_CATALOG[provider][0][0] |
| model_key = f"llm_model_{provider}" |
| st.session_state.setdefault(model_key, default_model) |
| active_model = st.session_state[model_key] |
| active_label = next( |
| (label for mid, label in MODEL_CATALOG[provider] if mid == active_model), |
| active_model, |
| ) |
| short_label = active_label.split(" β ")[0] |
|
|
| with st.popover(f"β {PROVIDER_LABELS[provider]} Β· {short_label}", use_container_width=True): |
| st.caption(t("model_settings_title")) |
|
|
| selected_provider = st.radio( |
| t("model_provider_label"), |
| options=list(MODEL_CATALOG.keys()), |
| format_func=lambda p: PROVIDER_LABELS.get(p, p), |
| key="llm_provider", |
| horizontal=True, |
| ) |
|
|
| model_options = MODEL_CATALOG[selected_provider] |
| model_ids = [mid for mid, _label in model_options] |
| model_key = f"llm_model_{selected_provider}" |
| st.session_state.setdefault(model_key, model_options[0][0]) |
| selected_model = st.selectbox( |
| t("model_select_label"), |
| options=model_ids, |
| format_func=lambda mid: next((lbl for m, lbl in model_options if m == mid), mid), |
| key=model_key, |
| ) |
|
|
| key_state_key = f"llm_api_key_{selected_provider}" |
| user_key = st.text_input( |
| t("model_key_label"), |
| type="password", |
| key=key_state_key, |
| help=t("model_key_help"), |
| ) |
|
|
| if user_key and not key_looks_valid(selected_provider, user_key): |
| st.caption( |
| "β " + t("model_key_prefix_warning").format( |
| provider=PROVIDER_LABELS.get(selected_provider, selected_provider) |
| ) |
| ) |
|
|
| key, source = resolve_api_key(selected_provider, user_key) |
| if source == "env": |
| st.caption(t("model_status_env")) |
| elif source == "user": |
| st.caption(t("model_status_user")) |
| else: |
| st.warning( |
| t("model_status_missing").format(url=console_url(selected_provider)), |
| icon="β οΈ", |
| ) |
|
|
| if key is None: |
| return None, source |
| return RunConfig(provider=selected_provider, model=selected_model, api_key=user_key or None), source |
|
|