File size: 3,351 Bytes
7880373 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | """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
|