Cheatsheets — vue longue
retour à la listeToutes les pages concaténées sur un seul document, pour un Ctrl-F direct.
Gradio
page dédiée →Interfaces de démo pour modèles ML. Contrairement à streamlit, le modèle est événementiel : on déclare des composants et on branche des fonctions dessus, sans réexécution complète du script.
uv add gradio
python app.py # sert sur http://127.0.0.1:7860
Le plus court chemin
import gradio as gr
def check(site: str, produit: str) -> str:
return f"{produit} trouvé sur {site} en 3 clics"
gr.Interface(
fn=check,
inputs=["text", "text"],
outputs="text",
title="Findability checker",
).launch()
* Running on local URL: http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
share=True ouvre un tunnel public valable 72 h — pratique pour montrer une démo à un
client sans rien déployer.
Blocks, pour tout le reste
Interface couvre le cas « une fonction, des entrées, des sorties ». Dès qu'il faut de la
mise en page ou plusieurs interactions, passer à Blocks.
with gr.Blocks(title="Findability") as demo:
gr.Markdown("## Test d'accessibilité produit")
with gr.Row():
site = gr.Textbox(label="Site", value="versace.com")
produit = gr.Textbox(label="Produit")
with gr.Row():
budget = gr.Slider(10, 200, value=60, step=10, label="Budget d'étapes")
mode = gr.Radio(["fast", "default"], value="default", label="Mode")
run = gr.Button("Lancer", variant="primary")
out = gr.JSON(label="Résultat")
gallery = gr.Gallery(label="Captures", columns=3)
run.click(fn=check, inputs=[site, produit, budget, mode], outputs=[out, gallery])
demo.launch()
inputs et outputs sont des listes de composants, et la fonction doit renvoyer autant
de valeurs qu'il y a de sorties, dans le même ordre.
Composants courants
| Composant | Usage |
|---|---|
gr.Textbox(lines=5) |
texte court ou long |
gr.Number, gr.Slider |
valeurs numériques |
gr.Dropdown(choices=[...], multiselect=True) |
listes |
gr.Checkbox, gr.Radio |
booléens et choix exclusifs |
gr.Image(type="pil") |
image en entrée ou sortie |
gr.File, gr.Audio, gr.Video |
fichiers |
gr.Dataframe |
tableau éditable |
gr.JSON, gr.Label |
sorties structurées |
gr.Plot |
figure matplotlib ou plotly |
Événements
run.click(fn, inputs=..., outputs=...)
site.change(fn, inputs=site, outputs=out)
site.submit(fn, ...) # touche Entrée
demo.load(fn, ...) # au chargement de la page
Enchaîner des étapes en gardant l'interface réactive :
run.click(prepare, [site], [status]).then(execute, [site], [out])
Streaming
def stream(prompt):
partial = ""
for chunk in llm.stream(prompt):
partial += chunk
yield partial # un yield = une mise à jour de l'UI
gr.Interface(fn=stream, inputs="text", outputs="text").launch()
Un générateur suffit : chaque yield pousse une nouvelle valeur au composant de sortie.
Chat
def respond(message, history):
return f"Reçu : {message}"
gr.ChatInterface(respond, type="messages").launch()
history arrive en liste de dicts {"role", "content"} avec type="messages". C'est le
format aligné sur les API LLM, à préférer à l'ancien format en tuples.
État par session
with gr.Blocks() as demo:
state = gr.State([]) # propre à chaque visiteur
def add(item, current):
current = current + [item]
return current, current
box = gr.Textbox()
out = gr.JSON()
box.submit(add, [box, state], [state, out])
gr.State n'est jamais rendu, il transporte des données Python entre événements.
Files d'attente et progression
def long_task(x, progress=gr.Progress()):
for i in progress.tqdm(range(10), desc="Sessions"):
run(i)
return "ok"
demo.queue(max_size=20).launch()
queue() est nécessaire dès que plusieurs personnes utilisent la démo en même temps.
Déployer sur Hugging Face Spaces
Un repo avec app.py, requirements.txt, et un README.md à en-tête :
---
title: Findability Checker
sdk: gradio
sdk_version: "5.0.0"
app_file: app.py
---
Les secrets se règlent dans les paramètres du Space et se lisent par os.environ.
Gradio ou Streamlit
Gradio pour exposer un modèle ou une fonction, surtout avec image, audio ou chat, et pour publier sur Spaces. Streamlit pour un tableau de bord de données avec des filtres. Les deux sont des outils de démo : au-delà, une vraie application.
See also
Streamlit
page dédiée →Interface web en Python pur. Le meilleur rapport temps/effet pour une démo client interne.
Le modèle d'exécution, à comprendre en premier
Le script entier est réexécuté de haut en bas à chaque interaction. Pas de callbacks, pas de composants. C'est ce qui rend Streamlit simple et ce qui provoque toutes ses surprises.
Deux conséquences : tout état doit vivre dans st.session_state, et tout calcul coûteux
doit être mis en cache.
pip install streamlit
streamlit run app.py
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8501
Network URL: http://192.168.1.24:8501
Afficher
import streamlit as st
st.title("Test d'accessibilité produit")
st.header("Résultats")
st.subheader("Par site")
st.write("Accepte à peu près tout : texte, DataFrame, figure, dict")
st.markdown("**gras**, `code`, [lien](https://example.com)")
st.code("requests.post(url, json=payload)", language="python")
st.json({"findable": True, "clicks": 3})
st.dataframe(df) # interactif, triable
st.table(df.head()) # statique
st.metric("Clics moyens", 3.2, delta=-0.4)
Saisir
site = st.text_input("Site", value="versace.com")
produit = st.text_area("Produit recherché")
budget = st.slider("Budget d'étapes", 10, 200, 60)
n = st.number_input("Répétitions", min_value=1, max_value=10, value=3)
mode = st.selectbox("Mode", ["fast", "default"])
tags = st.multiselect("Tests", ["navigation", "panier", "checkout"])
strict = st.checkbox("Mode strict")
fichier = st.file_uploader("CSV", type=["csv"])
if st.button("Lancer"):
...
Chaque widget renvoie sa valeur courante. Le if st.button(...) n'est vrai que sur le
rerun déclenché par le clic.
Mise en page
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Sessions", 12)
with st.sidebar:
api_key = st.text_input("Clé API", type="password")
tab1, tab2 = st.tabs(["Résultats", "Logs"])
with tab1:
st.dataframe(df)
with st.expander("Détails techniques"):
st.code(trace)
with st.container():
st.write("bloc regroupé")
État
if "runs" not in st.session_state:
st.session_state.runs = []
if st.button("Ajouter"):
st.session_state.runs.append({"site": site})
st.write(f"{len(st.session_state.runs)} exécutions")
Sans session_state, la liste serait recréée vide à chaque interaction.
Cache
@st.cache_data # pour des données : DataFrame, JSON, réponses API
def charger(path: str):
return pd.read_csv(path)
@st.cache_resource # pour des objets vivants : client, modèle, connexion
def get_client():
return SomeClient(api_key=os.environ["API_KEY"])
La distinction compte : cache_data sérialise et renvoie une copie, cache_resource
renvoie le même objet partagé entre sessions. Un client HTTP dans cache_data casse.
Retour visuel pendant un traitement long
with st.spinner("Session en cours…"):
result = run_agent(site, produit)
bar = st.progress(0)
for i, test in enumerate(tests):
run(test)
bar.progress((i + 1) / len(tests))
st.success("Terminé")
st.warning("2 tests non concluants")
st.error("Clé API invalide")
with st.status("Navigation…", expanded=True) as s:
st.write("Ouverture du site")
st.write("Recherche du produit")
s.update(label="Terminé", state="complete")
Streaming d'un LLM
def tokens():
for chunk in client.stream(prompt):
yield chunk.text
st.write_stream(tokens)
Pour un chat complet, st.chat_message("user" | "assistant") et st.chat_input().
Secrets et configuration
.streamlit/secrets.toml :
API_KEY = "sk-..."
key = st.secrets["API_KEY"]
.streamlit/config.toml pour le thème et le port :
[server]
port = 8501
[theme]
base = "light"
Limites à connaître avant de s'engager
Pas de routing multi-pages fin (juste un dossier pages/), peu de contrôle sur le CSS, et
le modèle de rerun devient pénible dès qu'on veut une vraie interactivité. Au-delà de la
démo, passer à Next.js avec une API FastAPI derrière.
Déploiement : Streamlit Community Cloud gratuit depuis un repo GitHub, ou un conteneur Docker n'importe où. Pas sur Vercel — Vercel ne fait pas tourner de processus Python long.