axiom-coding-agent-setup 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/agent-browser/SKILL.md +55 -0
- package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
- package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
- package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
- package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
- package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
- package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
- package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
- package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
- package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
- package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
- package/.agents/skills/fastapi/SKILL.md +436 -0
- package/.agents/skills/fastapi/references/dependencies.md +142 -0
- package/.agents/skills/fastapi/references/other-tools.md +76 -0
- package/.agents/skills/fastapi/references/streaming.md +105 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/gradio/SKILL.md +84 -0
- package/.agents/skills/gradio/references/api-signatures.md +115 -0
- package/.agents/skills/gradio/references/event-listeners.md +130 -0
- package/.agents/skills/gradio/references/examples.md +613 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/.agents/skills/ui-ux-pro-max/data +1 -0
- package/.agents/skills/ui-ux-pro-max/scripts +1 -0
- package/.agents/stack.md +187 -187
- package/.env.axiom +8 -0
- package/AGENTS.md +2 -1
- package/README.md +19 -3
- package/bin/cli.js +11 -1
- package/opencode.json +12 -4
- package/package.json +1 -1
- package/skills-lock.json +36 -0
- package/.agents/skills/n8n-patterns/SKILL.md +0 -272
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: improving-streamlit-design
|
|
3
|
+
description: Improving visual design in Streamlit apps. Use when polishing apps with icons, badges, spacing, or text styling. Covers Material icons, badge syntax, divider alternatives, and text casing conventions.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Streamlit visual design
|
|
8
|
+
|
|
9
|
+
Small touches that make apps feel polished.
|
|
10
|
+
|
|
11
|
+
**Related skills:** Visual design works hand-in-hand with other skills:
|
|
12
|
+
- `choosing-streamlit-selection-widgets` → Choosing the right widget (segmented control, pills, toggle)
|
|
13
|
+
- `displaying-streamlit-data` → Column config, sparklines, bordered metrics
|
|
14
|
+
- `using-streamlit-layouts` → Containers, alignment, dashboard cards
|
|
15
|
+
|
|
16
|
+
## Page config
|
|
17
|
+
|
|
18
|
+
Set browser tab title, icon, and layout. Place this at the top of your script to avoid visual blinking:
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
st.set_page_config(
|
|
22
|
+
page_title="My Dashboard",
|
|
23
|
+
page_icon=":material/analytics:",
|
|
24
|
+
layout="wide", # Use "wide" for dashboards with lots of data
|
|
25
|
+
)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Layout options:**
|
|
29
|
+
- `layout="centered"` (default) → Best for most apps, content is constrained to a readable width
|
|
30
|
+
- `layout="wide"` → Full-width, good for dashboards and data-heavy apps
|
|
31
|
+
|
|
32
|
+
## App logo
|
|
33
|
+
|
|
34
|
+
Add a logo to the sidebar/header:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
st.logo("logo.png")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Icons over emojis
|
|
41
|
+
|
|
42
|
+
Use Material icons for a cleaner, more professional look.
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# GOOD: Material icons
|
|
46
|
+
st.markdown(":material/settings:")
|
|
47
|
+
st.markdown(":material/calendar_today:")
|
|
48
|
+
st.markdown(":material/dashboard:")
|
|
49
|
+
st.markdown(":material/person:")
|
|
50
|
+
|
|
51
|
+
# SPARINGLY: Emojis for special occasions
|
|
52
|
+
st.markdown("Celebration! 🎉")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Format: `:material/icon_name:`
|
|
56
|
+
|
|
57
|
+
Find icons: https://fonts.google.com/icons
|
|
58
|
+
|
|
59
|
+
**Popular icons by category:**
|
|
60
|
+
|
|
61
|
+
| Category | Icons |
|
|
62
|
+
|----------|-------|
|
|
63
|
+
| Navigation | `home`, `arrow_back`, `menu`, `settings`, `search` |
|
|
64
|
+
| Actions | `send`, `play_arrow`, `refresh`, `download`, `upload`, `save`, `delete`, `edit` |
|
|
65
|
+
| Status | `check_circle`, `error`, `warning`, `info`, `pending` |
|
|
66
|
+
| Data | `table_chart`, `bar_chart`, `analytics`, `query_stats`, `database` |
|
|
67
|
+
| Content | `chat`, `code`, `description`, `article`, `folder` |
|
|
68
|
+
| UI | `visibility`, `build`, `tune`, `filter_list` |
|
|
69
|
+
|
|
70
|
+
## Badges for status
|
|
71
|
+
|
|
72
|
+
For standalone badges:
|
|
73
|
+
```python
|
|
74
|
+
st.badge("Active", icon=":material/check:", color="green")
|
|
75
|
+
st.badge("Pending", icon=":material/schedule:", color="orange")
|
|
76
|
+
st.badge("Deprecated", color="red")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For inline badges in text:
|
|
80
|
+
```python
|
|
81
|
+
st.markdown("""
|
|
82
|
+
:green-badge[Active] :orange-badge[Pending] :red-badge[Deprecated] :blue-badge[New]
|
|
83
|
+
""")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Avoid the old verbose syntax:
|
|
87
|
+
```python
|
|
88
|
+
# OLD (still works but cluttered)
|
|
89
|
+
st.markdown(":orange-background[:orange[Pending]]")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Spacing: remove dividers
|
|
93
|
+
|
|
94
|
+
Dividers (`st.divider()` or `---`) look heavy. Just remove them—Streamlit's default spacing is usually enough.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# BAD
|
|
98
|
+
st.header("Section 1")
|
|
99
|
+
st.write("Content")
|
|
100
|
+
st.divider() # Too heavy
|
|
101
|
+
st.header("Section 2")
|
|
102
|
+
|
|
103
|
+
# GOOD
|
|
104
|
+
st.header("Section 1")
|
|
105
|
+
st.write("Content")
|
|
106
|
+
st.header("Section 2")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If you genuinely need spacing:
|
|
110
|
+
```python
|
|
111
|
+
st.space("small") # Small gap
|
|
112
|
+
st.space("medium") # Medium gap
|
|
113
|
+
st.space("large") # Large gap
|
|
114
|
+
st.space(50) # Custom pixels for fine-tuning
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Don't** systematically replace dividers with `st.space()`—it can look weird too.
|
|
118
|
+
|
|
119
|
+
## Sentence casing
|
|
120
|
+
|
|
121
|
+
Use sentence casing for titles and labels. Title Case Feels Shouty.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
# GOOD
|
|
125
|
+
st.title("Upload your data")
|
|
126
|
+
st.selectbox("Select a region", options)
|
|
127
|
+
st.button("Save changes")
|
|
128
|
+
|
|
129
|
+
# BAD
|
|
130
|
+
st.title("Upload Your Data")
|
|
131
|
+
st.selectbox("Select A Region", options)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Caption over info
|
|
135
|
+
|
|
136
|
+
`st.info()` is too heavy for simple informational text.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# GOOD: Lighter
|
|
140
|
+
st.caption("Data last updated 5 minutes ago")
|
|
141
|
+
|
|
142
|
+
# BAD: Too heavy
|
|
143
|
+
st.info("Data last updated 5 minutes ago")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**When to use what:**
|
|
147
|
+
- `st.caption` → Simple info, metadata, timestamps
|
|
148
|
+
- `st.info` → Important instructions
|
|
149
|
+
- `st.warning` → Caution, potential issues
|
|
150
|
+
- `st.error` → Errors that block progress
|
|
151
|
+
- `st.success` → Confirmation of action
|
|
152
|
+
- `st.toast` → Lightweight confirmation that auto-dismisses
|
|
153
|
+
|
|
154
|
+
## Text alignment
|
|
155
|
+
|
|
156
|
+
Use `text_alignment` for text elements:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
st.title("Centered title", text_alignment="center")
|
|
160
|
+
st.write("Right aligned", text_alignment="right")
|
|
161
|
+
st.caption("Justified text", text_alignment="justify")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Options: `"left"` (default), `"center"`, `"right"`, `"justify"`
|
|
165
|
+
|
|
166
|
+
**Note:** `horizontal_alignment` on containers positions elements but also sets their `text_alignment`. If you need different text alignment within a horizontally-aligned container, override `text_alignment` on the text element itself.
|
|
167
|
+
|
|
168
|
+
## Icons in callouts and expanders
|
|
169
|
+
|
|
170
|
+
Material icons can make callouts and expanders look nicer:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
st.info("Processing complete", icon=":material/check_circle:")
|
|
174
|
+
st.warning("Rate limit approaching", icon=":material/warning:")
|
|
175
|
+
st.error("Connection failed", icon=":material/error:")
|
|
176
|
+
st.success("Saved!", icon=":material/thumb_up:")
|
|
177
|
+
|
|
178
|
+
with st.expander("Settings", icon=":material/settings:"):
|
|
179
|
+
st.write("Configure your preferences")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Other elements like `st.button` and `st.tabs` also support icons—worth considering when it adds clarity.
|
|
183
|
+
|
|
184
|
+
## References
|
|
185
|
+
|
|
186
|
+
- [st.set_page_config](https://docs.streamlit.io/develop/api-reference/configuration/st.set_page_config)
|
|
187
|
+
- [st.logo](https://docs.streamlit.io/develop/api-reference/media/st.logo)
|
|
188
|
+
- [st.badge](https://docs.streamlit.io/develop/api-reference/text/st.badge)
|
|
189
|
+
- [st.space](https://docs.streamlit.io/develop/api-reference/layout/st.space)
|
|
190
|
+
- [st.markdown](https://docs.streamlit.io/develop/api-reference/text/st.markdown)
|
|
191
|
+
- [st.toast](https://docs.streamlit.io/develop/api-reference/status/st.toast)
|
package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: optimizing-streamlit-performance
|
|
3
|
+
description: Optimizing Streamlit app performance. Use when apps are slow, rerunning too often, or loading heavy content. Covers caching, fragments, and static vs dynamic widget choices.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Streamlit performance
|
|
8
|
+
|
|
9
|
+
Performance is the biggest win. Without caching and fragments, your app reruns everything on every interaction.
|
|
10
|
+
|
|
11
|
+
## Caching
|
|
12
|
+
|
|
13
|
+
### @st.cache_data for data
|
|
14
|
+
|
|
15
|
+
Use for any function that loads or computes data.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
# BAD: Recomputes on every rerun
|
|
19
|
+
def load_data(path):
|
|
20
|
+
return pd.read_csv(path)
|
|
21
|
+
|
|
22
|
+
# GOOD: Cached
|
|
23
|
+
@st.cache_data
|
|
24
|
+
def load_data(path):
|
|
25
|
+
return pd.read_csv(path)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### @st.cache_resource for connections
|
|
29
|
+
|
|
30
|
+
Use for connections, API clients, ML models—objects that can't be serialized.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
@st.cache_resource
|
|
34
|
+
def get_connection():
|
|
35
|
+
return st.connection("snowflake")
|
|
36
|
+
|
|
37
|
+
@st.cache_resource
|
|
38
|
+
def load_model():
|
|
39
|
+
return torch.load("model.pt")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Critical warning:** Never mutate `@st.cache_resource` returns—changes affect all users:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# BAD: Mutating shared resource
|
|
46
|
+
@st.cache_resource
|
|
47
|
+
def get_config():
|
|
48
|
+
return {"setting": "default"}
|
|
49
|
+
|
|
50
|
+
config = get_config()
|
|
51
|
+
config["setting"] = "custom" # Affects ALL users!
|
|
52
|
+
|
|
53
|
+
# GOOD: Copy before modifying
|
|
54
|
+
config = get_config().copy()
|
|
55
|
+
config["setting"] = "custom"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Cleanup with `on_release`:** Clean up resources when evicted from cache:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
def cleanup_connection(conn):
|
|
62
|
+
conn.close()
|
|
63
|
+
|
|
64
|
+
@st.cache_resource(on_release=cleanup_connection)
|
|
65
|
+
def get_database():
|
|
66
|
+
return create_connection()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### TTL for fresh data
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
@st.cache_data(ttl="5m") # 5 minutes
|
|
73
|
+
def get_metrics():
|
|
74
|
+
return api.fetch()
|
|
75
|
+
|
|
76
|
+
@st.cache_data(ttl="1h") # 1 hour
|
|
77
|
+
def load_reference_data():
|
|
78
|
+
return pd.read_csv("large_reference.csv")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Guidelines:**
|
|
82
|
+
- Real-time dashboards → `ttl="1m"` or less
|
|
83
|
+
- Metrics/reports → `ttl="5m"` to `ttl="15m"`
|
|
84
|
+
- Reference data → `ttl="1h"` or more
|
|
85
|
+
- Static data → No TTL
|
|
86
|
+
|
|
87
|
+
### Prevent unbounded cache growth
|
|
88
|
+
|
|
89
|
+
**Important:** Caches without `ttl` or `max_entries` can grow indefinitely and cause memory issues. For any cached function that stores changing objects (user-specific data, parameterized queries), set limits:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# BAD: Unbounded cache - memory will grow indefinitely
|
|
93
|
+
@st.cache_data
|
|
94
|
+
def get_user_data(user_id):
|
|
95
|
+
return fetch_user(user_id)
|
|
96
|
+
|
|
97
|
+
# GOOD: Bounded cache with TTL
|
|
98
|
+
@st.cache_data(ttl="1h")
|
|
99
|
+
def get_user_data(user_id):
|
|
100
|
+
return fetch_user(user_id)
|
|
101
|
+
|
|
102
|
+
# GOOD: Bounded cache with max entries
|
|
103
|
+
@st.cache_data(max_entries=100)
|
|
104
|
+
def get_user_data(user_id):
|
|
105
|
+
return fetch_user(user_id)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Use `ttl` for time-based expiration OR `max_entries` for size-based limits. You usually don't need both.
|
|
109
|
+
|
|
110
|
+
### Caching anti-patterns
|
|
111
|
+
|
|
112
|
+
**Don't cache functions that read widgets:**
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# BAD: Widget inside cached function
|
|
116
|
+
@st.cache_data
|
|
117
|
+
def filtered_data():
|
|
118
|
+
query = st.text_input("Query") # Widget inside cached function!
|
|
119
|
+
return df[df["name"].str.contains(query)]
|
|
120
|
+
|
|
121
|
+
# GOOD: Pass widget values as parameters
|
|
122
|
+
@st.cache_data
|
|
123
|
+
def filtered_data(query: str):
|
|
124
|
+
return df[df["name"].str.contains(query)]
|
|
125
|
+
|
|
126
|
+
query = st.text_input("Query")
|
|
127
|
+
result = filtered_data(query)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Cache at the right granularity:**
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
# BAD: Caching too much - new cache entry per filter value
|
|
134
|
+
@st.cache_data
|
|
135
|
+
def get_and_filter_data(filter_value):
|
|
136
|
+
data = load_all_data() # Expensive!
|
|
137
|
+
return data[data["col"] == filter_value]
|
|
138
|
+
|
|
139
|
+
# GOOD: Cache the expensive part, filter separately
|
|
140
|
+
@st.cache_data(ttl="1h")
|
|
141
|
+
def load_all_data():
|
|
142
|
+
return fetch_from_database()
|
|
143
|
+
|
|
144
|
+
data = load_all_data()
|
|
145
|
+
filtered = data[data["col"] == filter_value]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Fragments
|
|
149
|
+
|
|
150
|
+
Use `@st.fragment` to isolate reruns for self-contained UI pieces.
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
# BAD: Full app reruns
|
|
154
|
+
st.metric("Users", get_count())
|
|
155
|
+
if st.button("Refresh"):
|
|
156
|
+
st.rerun()
|
|
157
|
+
|
|
158
|
+
# GOOD: Only fragment reruns
|
|
159
|
+
@st.fragment
|
|
160
|
+
def live_metrics():
|
|
161
|
+
st.metric("Users", get_count())
|
|
162
|
+
st.button("Refresh")
|
|
163
|
+
|
|
164
|
+
live_metrics()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For auto-refreshing metrics, use `run_every`:
|
|
168
|
+
```python
|
|
169
|
+
@st.fragment(run_every="30s")
|
|
170
|
+
def auto_refresh_metrics():
|
|
171
|
+
st.metric("Users", get_count())
|
|
172
|
+
|
|
173
|
+
auto_refresh_metrics()
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Use for: live metrics, refresh buttons, interactive charts that don't affect global state.
|
|
177
|
+
|
|
178
|
+
## Forms to batch interactions
|
|
179
|
+
|
|
180
|
+
By default, every widget interaction triggers a full rerun. Use `st.form` to batch multiple inputs and only rerun on submit.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
# BAD: Reruns on every keystroke and selection
|
|
184
|
+
name = st.text_input("Name")
|
|
185
|
+
email = st.text_input("Email")
|
|
186
|
+
role = st.selectbox("Role", ["Admin", "User"])
|
|
187
|
+
|
|
188
|
+
# GOOD: Single rerun when user clicks Submit
|
|
189
|
+
with st.form("user_form"):
|
|
190
|
+
name = st.text_input("Name")
|
|
191
|
+
email = st.text_input("Email")
|
|
192
|
+
role = st.selectbox("Role", ["Admin", "User"])
|
|
193
|
+
submitted = st.form_submit_button("Submit")
|
|
194
|
+
|
|
195
|
+
if submitted:
|
|
196
|
+
save_user(name, email, role)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Use `border=False` for seamless inline forms that don't look like forms:
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
with st.form("search", border=False):
|
|
203
|
+
with st.container(horizontal=True):
|
|
204
|
+
query = st.text_input("Search", label_visibility="collapsed")
|
|
205
|
+
st.form_submit_button(":material/search:")
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**When to use forms:**
|
|
209
|
+
- Multiple related inputs (signup, filters, settings)
|
|
210
|
+
- Text inputs where typing triggers expensive operations
|
|
211
|
+
- Any UI where "submit" semantics make sense
|
|
212
|
+
|
|
213
|
+
**When NOT to use forms:** If inputs depend on each other (e.g., selecting a country should update available cities), forms won't work since there's no rerun until submit.
|
|
214
|
+
|
|
215
|
+
## Conditional rendering
|
|
216
|
+
|
|
217
|
+
**This is critical and often missed.**
|
|
218
|
+
|
|
219
|
+
Layout containers like `st.tabs`, `st.expander`, and `st.popover` always render all their content, even when hidden or collapsed.
|
|
220
|
+
|
|
221
|
+
To render content only when needed, use elements like `st.segmented_control`, `st.toggle`, or `@st.dialog` with conditional logic:
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
# BAD: Heavy content loads even when tab not visible
|
|
225
|
+
tab1, tab2 = st.tabs(["Light", "Heavy"])
|
|
226
|
+
with tab2:
|
|
227
|
+
expensive_chart() # Always computed!
|
|
228
|
+
|
|
229
|
+
# GOOD: Content only loads when selected
|
|
230
|
+
view = st.segmented_control("View", ["Light", "Heavy"])
|
|
231
|
+
if view == "Heavy":
|
|
232
|
+
expensive_chart() # Only computed when selected
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
# BAD: Expander content always loads
|
|
237
|
+
with st.expander("Advanced options"):
|
|
238
|
+
heavy_computation() # Runs even when collapsed!
|
|
239
|
+
|
|
240
|
+
# GOOD: Toggle controls loading
|
|
241
|
+
if st.toggle("Show advanced options"):
|
|
242
|
+
heavy_computation() # Only runs when toggled on
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Pre-computation
|
|
246
|
+
|
|
247
|
+
Move expensive work outside the main flow:
|
|
248
|
+
- Compute aggregations in SQL/dbt, not Python
|
|
249
|
+
- Pre-compute metrics in scheduled jobs
|
|
250
|
+
- Use materialized views for complex queries
|
|
251
|
+
|
|
252
|
+
## Large data handling
|
|
253
|
+
|
|
254
|
+
### For datasets under ~100M rows
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
@st.cache_data
|
|
258
|
+
def load_data():
|
|
259
|
+
return pd.read_parquet("large_file.parquet")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### For very large datasets (over ~100M rows)
|
|
263
|
+
|
|
264
|
+
> **Note:** This is only an escape hatch when serialization becomes too slow. In most cases, data this large shouldn't be loaded entirely into memory—prefer using a database that queries and loads data on demand.
|
|
265
|
+
|
|
266
|
+
`@st.cache_data` uses pickle which slows with huge data. Use `@st.cache_resource` instead:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
@st.cache_resource # No serialization overhead
|
|
270
|
+
def load_huge_data():
|
|
271
|
+
return pd.read_parquet("huge_file.parquet")
|
|
272
|
+
|
|
273
|
+
# WARNING: Don't mutate the returned DataFrame!
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Sampling for exploration
|
|
277
|
+
|
|
278
|
+
When exploring large datasets, load a random sample instead of the full data:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
@st.cache_data(ttl="1h")
|
|
282
|
+
def load_sample(n=10000):
|
|
283
|
+
df = pd.read_parquet("huge.parquet")
|
|
284
|
+
return df.sample(n=n)
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Multithreading
|
|
288
|
+
|
|
289
|
+
Custom threads cannot call Streamlit commands (no session context).
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
import threading
|
|
293
|
+
|
|
294
|
+
def fetch_in_background(url, results, index):
|
|
295
|
+
results[index] = requests.get(url).json() # No st.* calls!
|
|
296
|
+
|
|
297
|
+
# Collect results, then display in main thread
|
|
298
|
+
results = [None] * len(urls)
|
|
299
|
+
threads = [
|
|
300
|
+
threading.Thread(target=fetch_in_background, args=(url, results, i))
|
|
301
|
+
for i, url in enumerate(urls)
|
|
302
|
+
]
|
|
303
|
+
for t in threads:
|
|
304
|
+
t.start()
|
|
305
|
+
for t in threads:
|
|
306
|
+
t.join()
|
|
307
|
+
|
|
308
|
+
# Now display in main thread
|
|
309
|
+
for result in results:
|
|
310
|
+
st.write(result)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**Prefer alternatives when possible:**
|
|
314
|
+
- `@st.cache_data` for expensive computations
|
|
315
|
+
- `@st.fragment(run_every="5s")` for periodic updates
|
|
316
|
+
|
|
317
|
+
## References
|
|
318
|
+
|
|
319
|
+
- [Caching overview](https://docs.streamlit.io/develop/concepts/architecture/caching)
|
|
320
|
+
- [st.cache_data](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.cache_data)
|
|
321
|
+
- [st.cache_resource](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.cache_resource)
|
|
322
|
+
- [st.fragment](https://docs.streamlit.io/develop/api-reference/execution-flow/st.fragment)
|
|
323
|
+
- [st.form](https://docs.streamlit.io/develop/api-reference/execution-flow/st.form)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: organizing-streamlit-code
|
|
3
|
+
description: Organizing Streamlit code for maintainability. Use when structuring apps with separate modules and utilities. Covers separation of concerns, keeping UI code clean, and import patterns.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Streamlit code organization
|
|
8
|
+
|
|
9
|
+
For most simple apps, keep everything in one file—it's cleaner and more straightforward. The app file should read like a normal Python script for data processing, with a few Streamlit commands sprinkled in.
|
|
10
|
+
|
|
11
|
+
Name the main file `streamlit_app.py` (Streamlit's default).
|
|
12
|
+
|
|
13
|
+
## When to split
|
|
14
|
+
|
|
15
|
+
**Keep in one file (most apps):**
|
|
16
|
+
- Apps under ~1000 lines
|
|
17
|
+
- One-off scripts and prototypes
|
|
18
|
+
- Apps where logic is straightforward
|
|
19
|
+
|
|
20
|
+
**Consider splitting when:**
|
|
21
|
+
- Data processing is complex (50+ lines of non-UI code)
|
|
22
|
+
- Multiple pages share logic
|
|
23
|
+
- You want to test business logic separately
|
|
24
|
+
|
|
25
|
+
If splitting makes sense, here's how to organize it.
|
|
26
|
+
|
|
27
|
+
## Directory structure
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
my-app/
|
|
31
|
+
├── streamlit_app.py # Main entry point
|
|
32
|
+
├── app_pages/ # Page UI modules
|
|
33
|
+
│ ├── dashboard.py
|
|
34
|
+
│ └── settings.py
|
|
35
|
+
└── utils/ # Business logic & helpers
|
|
36
|
+
├── data.py
|
|
37
|
+
└── api.py
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Separating UI from logic
|
|
41
|
+
|
|
42
|
+
When you do split, keep Streamlit files focused on UI and move complex logic to utility modules:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
# streamlit_app.py - UI-focused
|
|
46
|
+
import streamlit as st
|
|
47
|
+
from utils.data import load_sales_data, compute_metrics
|
|
48
|
+
|
|
49
|
+
st.title("Sales Dashboard")
|
|
50
|
+
|
|
51
|
+
start = st.date_input("Start")
|
|
52
|
+
end = st.date_input("End")
|
|
53
|
+
|
|
54
|
+
data = load_sales_data(start, end)
|
|
55
|
+
metrics = compute_metrics(data)
|
|
56
|
+
|
|
57
|
+
st.metric("Revenue", f"${metrics['revenue']:,.0f}")
|
|
58
|
+
st.dataframe(data)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Avoid if __name__ == "__main__"
|
|
62
|
+
|
|
63
|
+
Streamlit apps run the entire file on each interaction. Don't use the main guard in Streamlit files.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# BAD - don't do this in streamlit_app.py or pages
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
main()
|
|
69
|
+
|
|
70
|
+
# GOOD - just put the code directly
|
|
71
|
+
import streamlit as st
|
|
72
|
+
|
|
73
|
+
st.title("My App")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The main guard is fine in utility modules for quick testing:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# utils/data.py
|
|
80
|
+
def load_data(path):
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
# Optional: test this module directly with `python utils/data.py`
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
print(load_data("test.csv"))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## References
|
|
89
|
+
|
|
90
|
+
- [Multipage apps](https://docs.streamlit.io/develop/concepts/multipage-apps)
|
|
91
|
+
- [st.cache_data](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.cache_data)
|