forked from kinosal/tweet
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
236 lines (211 loc) · 8.69 KB
/
app.py
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
"""Streamlit app to generate Tweets."""
# Import from standard library
import logging
import random
import re
# Import from 3rd party libraries
import streamlit as st
import streamlit.components.v1 as components
# Import modules
import tweets as twe
import oai
# Configure logger
logging.basicConfig(format="\n%(asctime)s\n%(message)s", level=logging.INFO, force=True)
# Define functions
def generate_text(topic: str, mood: str = "", style: str = ""):
"""Generate Tweet text."""
if st.session_state.n_requests >= 5:
st.session_state.text_error = "Too many requests. Please wait a few seconds before generating another Tweet."
logging.info(f"Session request limit reached: {st.session_state.n_requests}")
st.session_state.n_requests = 1
return
st.session_state.tweet = ""
st.session_state.image = ""
st.session_state.text_error = ""
if not topic:
st.session_state.text_error = "Please enter a topic"
return
with text_spinner_placeholder:
with st.spinner("Please wait while your Tweet is being generated..."):
mood_prompt = f"{mood} " if mood else ""
if style:
twitter = twe.Tweets(account=style)
tweets = twitter.fetch_tweets()
tweets_prompt = "\n\n".join(tweets)
prompt = (
f"Write a {mood_prompt}Tweet about {topic} in less than 120 characters "
f"and in the style of the following Tweets:\n\n{tweets_prompt}\n\n"
)
else:
prompt = f"Write a {mood_prompt}Tweet about {topic} in less than 120 characters:\n\n"
openai = oai.Openai()
flagged = openai.moderate(prompt)
mood_output = f", Mood: {mood}" if mood else ""
style_output = f", Style: {style}" if style else ""
if flagged:
st.session_state.text_error = "Input flagged as inappropriate."
logging.info(f"Topic: {topic}{mood_output}{style_output}\n")
return
else:
st.session_state.text_error = ""
st.session_state.n_requests += 1
st.session_state.tweet = (
openai.complete(prompt).strip().replace('"', "")
)
logging.info(
f"Topic: {topic}{mood_output}{style_output}\n"
f"Tweet: {st.session_state.tweet}"
)
def generate_image(prompt: str):
"""Generate Tweet image."""
if st.session_state.n_requests >= 5:
st.session_state.text_error = "Too many requests. Please wait a few seconds before generating another text or image."
logging.info(f"Session request limit reached: {st.session_state.n_requests}")
st.session_state.n_requests = 1
return
with image_spinner_placeholder:
with st.spinner("Please wait while your image is being generated..."):
openai = oai.Openai()
prompt_wo_hashtags = re.sub("#[A-Za-z0-9_]+", "", prompt)
processing_prompt = (
"Create a detailed but brief description of an image that captures "
f"the essence of the following text:\n{prompt_wo_hashtags}\n\n"
)
processed_prompt = (
openai.complete(
prompt=processing_prompt, temperature=0.5, max_tokens=40
)
.strip()
.replace('"', "")
.split(".")[0]
+ "."
)
st.session_state.n_requests += 1
st.session_state.image = openai.image(processed_prompt)
logging.info(f"Tweet: {prompt}\nImage prompt: {processed_prompt}")
# Configure Streamlit page and state
st.set_page_config(page_title="Tweet", page_icon="🤖")
if "tweet" not in st.session_state:
st.session_state.tweet = ""
if "image" not in st.session_state:
st.session_state.image = ""
if "text_error" not in st.session_state:
st.session_state.text_error = ""
if "image_error" not in st.session_state:
st.session_state.image_error = ""
if "feeling_lucky" not in st.session_state:
st.session_state.feeling_lucky = False
if "n_requests" not in st.session_state:
st.session_state.n_requests = 0
# Force responsive layout for columns also on mobile
st.write(
"""<style>
[data-testid="column"] {
width: calc(50% - 1rem);
flex: 1 1 calc(50% - 1rem);
min-width: calc(50% - 1rem);
}
</style>""",
unsafe_allow_html=True,
)
# Render Streamlit page
st.title("Generate Tweets")
st.markdown(
"This mini-app generates Tweets using OpenAI's GPT-3 based [Davinci model](https://beta.openai.com/docs/models/overview) for texts and [DALL·E](https://beta.openai.com/docs/guides/images) for images. You can find the code on [GitHub](https://github.com/kinosal/tweet) and the author on [Twitter](https://twitter.com/kinosal)."
)
topic = st.text_input(label="Topic (or hashtag)", placeholder="AI")
mood = st.text_input(
label="Mood (e.g. inspirational, funny, serious) (optional)",
placeholder="inspirational",
)
style = st.text_input(
label="Twitter account handle to style-copy recent Tweets (optional)",
placeholder="elonmusk",
)
col1, col2 = st.columns(2)
with col1:
st.session_state.feeling_lucky = not st.button(
label="Generate text",
type="primary",
on_click=generate_text,
args=(topic, mood, style),
)
with col2:
with open("moods.txt") as f:
sample_moods = f.read().splitlines()
st.session_state.feeling_lucky = st.button(
label="Feeling lucky",
type="secondary",
on_click=generate_text,
args=("an interesting topic", random.choice(sample_moods), ""),
)
text_spinner_placeholder = st.empty()
if st.session_state.text_error:
st.error(st.session_state.text_error)
if st.session_state.tweet:
st.markdown("""---""")
st.text_area(label="Tweet", value=st.session_state.tweet, height=100)
col1, col2 = st.columns(2)
with col1:
components.html(
f"""
<a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-size="large" data-text="{st.session_state.tweet}\n - Tweet generated via" data-url="https://tweets.streamlit.app" data-show-count="false">Tweet</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
""",
height=45,
)
with col2:
if st.session_state.feeling_lucky:
st.button(
label="Regenerate text",
type="secondary",
on_click=generate_text,
args=("an interesting topic", random.choice(sample_moods), ""),
)
else:
st.button(
label="Regenerate text",
type="secondary",
on_click=generate_text,
args=(topic, mood, style),
)
if not st.session_state.image:
st.button(
label="Generate image",
type="primary",
on_click=generate_image,
args=[st.session_state.tweet],
)
else:
st.image(st.session_state.image)
st.button(
label="Regenerate image",
type="secondary",
on_click=generate_image,
args=[st.session_state.tweet],
)
image_spinner_placeholder = st.empty()
if st.session_state.image_error:
st.error(st.session_state.image_error)
st.markdown("""---""")
col1, col2 = st.columns(2)
with col1:
st.markdown(
"**Other Streamlit apps by [@kinosal](https://twitter.com/kinosal)**"
)
st.markdown("[Twitter Wrapped](https://twitter-likes.streamlit.app)")
st.markdown("[Content Summarizer](https://web-summarizer.streamlit.app)")
st.markdown("[Code Translator](https://english-to-code.streamlit.app)")
st.markdown("[PDF Analyzer](https://pdf-keywords.streamlit.app)")
with col2:
st.write("If you like this app, please consider to")
components.html(
"""
<form action="https://www.paypal.com/donate" method="post" target="_top">
<input type="hidden" name="hosted_button_id" value="8JJTGY95URQCQ" />
<input type="image" src="https://pics.paypal.com/00/s/MDY0MzZhODAtNGI0MC00ZmU5LWI3ODYtZTY5YTcxOTNlMjRm/file.PNG" height="35" border="0" name="submit" title="Donate with PayPal" alt="Donate with PayPal button" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
</form>
""",
height=45,
)
st.write("so I can keep it alive. Thank you!")