Streamlit Dashboards
Skills that store data (SQLite, JSON logs) can include a Streamlit dashboard for visualization. The dashboard uses synchronous sqlite3 + pandas for read-only access, avoiding conflicts with the skill’s async runtime.
When to add a dashboard
Add a dashboard when your skill accumulates data over time — audit logs, scan results, monitoring metrics. The dashboard provides a visual interface for exploring that data.
Directory structure
my-skill/
├── skill/
├── src/my_skill/
├── dashboard/
│ ├── app.py # Streamlit entry point
│ ├── db_reader.py # Sync data access
│ ├── charts.py # Reusable chart components
│ └── requirements.txt # streamlit, pandas
└── data/ # Runtime data (gitignored)
└── my_skill.dbThe async problem
Skills use async libraries (httpx, Tortoise ORM) for their runtime. Streamlit is synchronous and runs in a separate process. Sharing an async database connection is not possible.
Solution: use sqlite3 + pandas for read-only access in the dashboard. The skill writes to SQLite via Tortoise ORM; the dashboard reads via sqlite3.
Dashboard entry point
dashboard/app.py:
import streamlit as st
import pandas as pd
from db_reader import load_data
st.set_page_config(page_title="My Skill Dashboard", layout="wide")
# Guard: stop if no data
df = load_data()
if df.empty:
st.info("No data yet. Run the skill first.")
st.stop()
# Sidebar filters
with st.sidebar:
status = st.selectbox("Status", ["all"] + df["status"].unique().tolist())
if status != "all":
df = df[df["status"] == status]
# Main content
st.title("My Skill Results")
st.dataframe(df, use_container_width=True)st.set_page_config() must be the first Streamlit call. Place it before any other st.* function.
Database reader
dashboard/db_reader.py:
import sqlite3
from pathlib import Path
import pandas as pd
DB_PATH = Path(__file__).resolve().parent.parent / "data" / "my_skill.db"
def load_data() -> pd.DataFrame:
if not DB_PATH.exists():
return pd.DataFrame()
conn = sqlite3.connect(str(DB_PATH))
df = pd.read_sql_query("SELECT * FROM results ORDER BY created_at DESC", conn)
conn.close()
return dfDerive DB_PATH from __file__ — never hardcode absolute paths.
Dashboard dependencies
dashboard/requirements.txt:
streamlit>=1.40.0
pandas>=2.0.0Running the dashboard
pip install -r dashboard/requirements.txt
streamlit run dashboard/app.pyOpens at http://localhost:8501. Use --server.port 8502 for a custom port.
Auto-refresh
For monitoring dashboards that need live updates:
import time
placeholder = st.empty()
while True:
df = load_data()
with placeholder.container():
st.dataframe(df, use_container_width=True)
time.sleep(30)
st.rerun()Entry point in pyproject.toml
Optionally add a script entry point for convenience:
[project.scripts]
my-skill = "my_skill._skill:main"
my-skill-dashboard = "streamlit:run dashboard/app.py"