Skip to content

Commit 7699fe5

Browse files
Merge branch 'main' into test-sis
Former-commit-id: 001fa72
2 parents f18964b + 8289844 commit 7699fe5

File tree

6 files changed

+197
-3
lines changed

6 files changed

+197
-3
lines changed

content/develop/concepts/app-design/button-behavior-and-examples.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ begin.text_input('Name', key='name')
306306

307307
### Buttons to add other widgets dynamically
308308

309-
When dynamically adding widgets to the page, make sure to use an index to keep the keys unique and avoid a `DuplicateWidgetID` error. In this example, we define a function `display_input_row` which renders a row of widgets. That function accepts an `index` as a parameter. The widgets rendered by `display_input_row` use `index` within their keys so that `dispaly_input_row` can be executed multiple times on a single script rerun without repeating any widget keys.
309+
When dynamically adding widgets to the page, make sure to use an index to keep the keys unique and avoid a `DuplicateWidgetID` error. In this example, we define a function `display_input_row` which renders a row of widgets. That function accepts an `index` as a parameter. The widgets rendered by `display_input_row` use `index` within their keys so that `display_input_row` can be executed multiple times on a single script rerun without repeating any widget keys.
310310

311311
```python
312312
import streamlit as st

content/develop/concepts/app-design/custom-classes.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ We begin by covering some general-purpose patterns you can use for different typ
1919

2020
### Pattern 1: Define your class in a separate module
2121

22-
This is the recommended, general solution. If possible, move class definitions into their own module file and import them into your app script. As long as you are not editing the file where your class is defined, Streamlit will not re-import it with each rerun. Therefore, if a class is defined in an external file and imported into your script, the class will not be redefined during the session.
22+
This is the recommended, general solution. If possible, move class definitions into their own module file and import them into your app script. As long as you are not editing the files that define your app, Streamlit will not re-import those classes with each rerun. Therefore, if a class is defined in an external file and imported into your script, the class will not be redefined during the session, unless you are actively editing your app.
2323

2424
#### Example: Move your class definition
2525

@@ -74,7 +74,7 @@ st.write(isinstance(st.session_state.my_instance, MyClass))
7474
st.button("Rerun")
7575
```
7676

77-
Streamlit only reloads code in imported modules when it detects the code has changed. Thus, if you are actively editing the file where your class is defined, you may need to stop and restart your Streamlit server to avoid an undesirable class redefinition mid-session.
77+
Streamlit only reloads code in imported modules when it detects the code has changed. Thus, if you are actively editing your app code, you may need to start a new session or restart your Streamlit server to avoid an undesirable class redefinition.
7878

7979
### Pattern 2: Force your class to compare internal values
8080

content/develop/concepts/architecture/caching.md

+1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ Or if you're lazy and don't want to think too much, look up your use case or ret
289289
| Running an ML model (inference) | pandas.DataFrame, str, int, dict, list | st.cache_data |
290290
| Creating or processing images | PIL.Image.Image, numpy.ndarray | st.cache_data |
291291
| Creating charts | matplotlib.figure.Figure, plotly.graph_objects.Figure, altair.Chart | st.cache_data (but some libraries require st.cache_resource, since the chart object is not serializable – make sure not to mutate the chart after creation!) |
292+
| Lazy computations | polars.LazyFrame | st.cache_resource (but may be better to use st.cache_data on the collected results) |
292293
| Loading ML models | transformers.Pipeline, torch.nn.Module, tensorflow.keras.Model | st.cache_resource |
293294
| Initializing database connections | pyodbc.Connection, sqlalchemy.engine.base.Engine, psycopg2.connection, mysql.connector.MySQLConnection, sqlite3.Connection | st.cache_resource |
294295
| Opening persistent file handles | \_io.TextIOWrapper | st.cache_resource |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lorem
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import streamlit as st
2+
import time
3+
4+
5+
def chat_stream(prompt):
6+
response = f'You said, "{prompt}" ...interesting.'
7+
for char in response:
8+
yield char
9+
time.sleep(0.02)
10+
11+
12+
def save_feedback(index):
13+
st.session_state.history[index]["feedback"] = st.session_state[f"feedback_{index}"]
14+
15+
16+
if "history" not in st.session_state:
17+
st.session_state.history = []
18+
19+
for i, message in enumerate(st.session_state.history):
20+
with st.chat_message(message["role"]):
21+
st.write(message["content"])
22+
if message["role"] == "assistant":
23+
feedback = message.get("feedback", None)
24+
st.session_state[f"feedback_{i}"] = feedback
25+
st.feedback(
26+
"thumbs",
27+
key=f"feedback_{i}",
28+
disabled=feedback is not None,
29+
on_change=save_feedback,
30+
args=[i],
31+
)
32+
33+
if prompt := st.chat_input("Say something"):
34+
with st.chat_message("user"):
35+
st.write(prompt)
36+
st.session_state.history.append({"role": "user", "content": prompt})
37+
with st.chat_message("assistant"):
38+
response = st.write_stream(chat_stream(prompt))
39+
st.feedback(
40+
"thumbs",
41+
key=f"feedback_{len(st.session_state.history)}",
42+
on_change=save_feedback,
43+
args=[len(st.session_state.history)],
44+
)
45+
st.session_state.history.append({"role": "assistant", "content": response})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import streamlit as st
2+
import lorem
3+
from random import randint
4+
import time
5+
6+
if "stage" not in st.session_state:
7+
st.session_state.stage = "user"
8+
st.session_state.history = []
9+
st.session_state.pending = None
10+
st.session_state.validation = {}
11+
12+
13+
def chat_stream():
14+
for i in range(randint(3, 9)):
15+
yield lorem.sentence() + " "
16+
time.sleep(0.2)
17+
18+
19+
def validate(response):
20+
response_sentences = response.split(". ")
21+
response_sentences = [
22+
sentence.strip(". ") + "."
23+
for sentence in response_sentences
24+
if sentence.strip(". ") != ""
25+
]
26+
validation_list = [
27+
True if sentence.count(" ") > 4 else False for sentence in response_sentences
28+
]
29+
return response_sentences, validation_list
30+
31+
32+
def add_highlights(response_sentences, validation_list, bg="red", text="red"):
33+
return [
34+
f":{text}[:{bg}-background[" + sentence + "]]" if not is_valid else sentence
35+
for sentence, is_valid in zip(response_sentences, validation_list)
36+
]
37+
38+
39+
for message in st.session_state.history:
40+
with st.chat_message(message["role"]):
41+
st.write(message["content"])
42+
43+
if st.session_state.stage == "user":
44+
if user_input := st.chat_input("Enter a prompt"):
45+
st.session_state.history.append({"role": "user", "content": user_input})
46+
with st.chat_message("user"):
47+
st.write(user_input)
48+
with st.chat_message("assistant"):
49+
response = st.write_stream(chat_stream())
50+
st.session_state.pending = response
51+
st.session_state.stage = "validate"
52+
st.rerun()
53+
54+
elif st.session_state.stage == "validate":
55+
st.chat_input("Accept, correct, or rewrite the answer above.", disabled=True)
56+
response_sentences, validation_list = validate(st.session_state.pending)
57+
highlighted_sentences = add_highlights(response_sentences, validation_list)
58+
with st.chat_message("assistant"):
59+
st.markdown(" ".join(highlighted_sentences))
60+
st.divider()
61+
cols = st.columns(3)
62+
if cols[0].button(
63+
"Correct errors", type="primary", disabled=all(validation_list)
64+
):
65+
st.session_state.validation = {
66+
"sentences": response_sentences,
67+
"valid": validation_list,
68+
}
69+
st.session_state.stage = "correct"
70+
st.rerun()
71+
if cols[1].button("Accept"):
72+
st.session_state.history.append(
73+
{"role": "assistant", "content": st.session_state.pending}
74+
)
75+
st.session_state.pending = None
76+
st.session_state.validation = {}
77+
st.session_state.stage = "user"
78+
st.rerun()
79+
if cols[2].button("Rewrite answer", type="tertiary"):
80+
st.session_state.stage = "rewrite"
81+
st.rerun()
82+
83+
elif st.session_state.stage == "correct":
84+
st.chat_input("Accept, correct, or rewrite the answer above.", disabled=True)
85+
response_sentences = st.session_state.validation["sentences"]
86+
validation_list = st.session_state.validation["valid"]
87+
highlighted_sentences = add_highlights(
88+
response_sentences, validation_list, "gray", "gray"
89+
)
90+
if not all(validation_list):
91+
focus = validation_list.index(False)
92+
highlighted_sentences[focus] = ":red[:red" + highlighted_sentences[focus][11:]
93+
else:
94+
focus = None
95+
with st.chat_message("assistant"):
96+
st.markdown(" ".join(highlighted_sentences))
97+
st.divider()
98+
if focus is not None:
99+
new_sentence = st.text_input(
100+
"Replacement text:", value=response_sentences[focus]
101+
)
102+
cols = st.columns(2)
103+
if cols[0].button(
104+
"Update", type="primary", disabled=len(new_sentence.strip()) < 1
105+
):
106+
st.session_state.validation["sentences"][focus] = (
107+
new_sentence.strip(". ") + "."
108+
)
109+
st.session_state.validation["valid"][focus] = True
110+
st.session_state.pending = " ".join(
111+
st.session_state.validation["sentences"]
112+
)
113+
st.rerun()
114+
if cols[1].button("Remove"):
115+
st.session_state.validation["sentences"].pop(focus)
116+
st.session_state.validation["valid"].pop(focus)
117+
st.session_state.pending = " ".join(
118+
st.session_state.validation["sentences"]
119+
)
120+
st.rerun()
121+
else:
122+
cols = st.columns(2)
123+
if cols[0].button("Accept", type="primary"):
124+
st.session_state.history.append(
125+
{"role": "assistant", "content": st.session_state.pending}
126+
)
127+
st.session_state.pending = None
128+
st.session_state.validation = {}
129+
st.session_state.stage = "user"
130+
st.rerun()
131+
if cols[1].button("Re-validate"):
132+
st.session_state.validation = {}
133+
st.session_state.stage = "validate"
134+
st.rerun()
135+
136+
elif st.session_state.stage == "rewrite":
137+
st.chat_input("Accept, correct, or rewrite the answer above.", disabled=True)
138+
with st.chat_message("assistant"):
139+
new = st.text_area("Rewrite the answer", value=st.session_state.pending)
140+
if st.button(
141+
"Update", type="primary", disabled=new is None or new.strip(". ") == ""
142+
):
143+
st.session_state.history.append({"role": "assistant", "content": new})
144+
st.session_state.pending = None
145+
st.session_state.validation = {}
146+
st.session_state.stage = "user"
147+
st.rerun()

0 commit comments

Comments
 (0)