Spaces:
Sleeping
Sleeping
Commit
·
60a9633
1
Parent(s):
4a6bf8c
Final Commit
Browse files- .gitignore +2 -0
- README.md +4 -4
- app.py +72 -0
- conversation.py +219 -0
- requirements.txt +62 -0
- static/Background.jpg +0 -0
- static/Logo.png +0 -0
- static/style.css +53 -0
- templates/index.html +66 -0
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
venv/
|
2 |
+
__pycache__/
|
README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
---
|
2 |
title: MailCraft
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: streamlit
|
7 |
-
sdk_version: 1.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: mit
|
|
|
1 |
---
|
2 |
title: MailCraft
|
3 |
+
emoji: 🏃
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: gray
|
6 |
sdk: streamlit
|
7 |
+
sdk_version: 1.28.2
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
license: mit
|
app.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
3 |
+
from conversation import get_default_conv_template
|
4 |
+
import base64
|
5 |
+
import streamlit as st
|
6 |
+
|
7 |
+
def generate(style, topic, words=200, sender='Sender_Name', recipient='Recipient_Name'):
|
8 |
+
tokenizer = AutoTokenizer.from_pretrained("GeneZC/MiniChat-3B", use_fast=False)
|
9 |
+
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
10 |
+
def get_model(device):
|
11 |
+
if device == 'cuda':
|
12 |
+
return AutoModelForCausalLM.from_pretrained("GeneZC/MiniChat-3B", use_cache=True, device_map="auto", torch_dtype=torch.float16).eval()
|
13 |
+
else:
|
14 |
+
return AutoModelForCausalLM.from_pretrained("GeneZC/MiniChat-3B", use_cache=True, device_map="cpu", torch_dtype=torch.float32).eval()
|
15 |
+
|
16 |
+
model = get_model(device)
|
17 |
+
conv = get_default_conv_template("minichat")
|
18 |
+
|
19 |
+
question = f"""
|
20 |
+
Generate an email with the following specifications and make sure to highlight the subject, according to the topic of the mail, in the very first line of the response:
|
21 |
+
- Style: {style}
|
22 |
+
- Word Limit: {words}
|
23 |
+
- Topic: {topic}
|
24 |
+
- Sender Name: {sender}
|
25 |
+
- Recipient Name: {recipient}
|
26 |
+
"""
|
27 |
+
|
28 |
+
conv.append_message(conv.roles[0], question)
|
29 |
+
conv.append_message(conv.roles[1], None)
|
30 |
+
prompt = conv.get_prompt()
|
31 |
+
input_ids = tokenizer([prompt]).input_ids
|
32 |
+
output_ids = model.generate(
|
33 |
+
torch.as_tensor(input_ids).to(device),
|
34 |
+
do_sample=True,
|
35 |
+
temperature=0.7,
|
36 |
+
max_new_tokens=2000,
|
37 |
+
)
|
38 |
+
output_ids = output_ids[0][len(input_ids[0]):]
|
39 |
+
output = tokenizer.decode(output_ids, skip_special_tokens=True).strip()
|
40 |
+
return output
|
41 |
+
|
42 |
+
@st.cache_data
|
43 |
+
def get_image(file):
|
44 |
+
with open(file, "rb") as f:
|
45 |
+
data = f.read()
|
46 |
+
return base64.b64encode(data).decode()
|
47 |
+
|
48 |
+
with open("./static/style.css") as style:
|
49 |
+
st.markdown(f"<style>{style.read()}</style>", unsafe_allow_html=True)
|
50 |
+
|
51 |
+
img = get_image("./static/bg.png")
|
52 |
+
bg_image = f"""
|
53 |
+
<style>
|
54 |
+
[data-testid="stAppViewContainer"]{{
|
55 |
+
background-image: url("data:image/png;base64,{img}");
|
56 |
+
background-size: cover;
|
57 |
+
}}
|
58 |
+
</style>
|
59 |
+
"""
|
60 |
+
st.markdown(bg_image, unsafe_allow_html=True)
|
61 |
+
st.title("MailCraft")
|
62 |
+
st.subheader("Unlock Seamless Email Excellence 📧 for Effortless Communication")
|
63 |
+
|
64 |
+
style = st.text_input("Enter Email Type", placeholder="Ex. Professional/Personal/Job Application")
|
65 |
+
words = st.number_input("Enter Word Limit", min_value=100, max_value=500, value=None, placeholder="From 100 to 500")
|
66 |
+
topic = st.text_input("Enter Email Topic")
|
67 |
+
sender = st.text_input("Enter Sender Name")
|
68 |
+
recipient = st.text_input("Enter Recipient Name")
|
69 |
+
|
70 |
+
if st.button("Generate Email"):
|
71 |
+
generated_email = generate(style, topic, words, sender, recipient)
|
72 |
+
st.write(generated_email)
|
conversation.py
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import dataclasses
|
2 |
+
from enum import auto, Enum
|
3 |
+
from typing import List, Tuple, Any
|
4 |
+
|
5 |
+
|
6 |
+
class SeparatorStyle(Enum):
|
7 |
+
"""Different separator style."""
|
8 |
+
|
9 |
+
ADD_COLON_SINGLE = auto()
|
10 |
+
ADD_COLON_TWO = auto()
|
11 |
+
NO_COLON_SINGLE = auto()
|
12 |
+
BAIZE = auto()
|
13 |
+
PHOENIX = auto()
|
14 |
+
MINICHAT = auto()
|
15 |
+
|
16 |
+
|
17 |
+
@dataclasses.dataclass
|
18 |
+
class Conversation:
|
19 |
+
"""A class that keeps all conversation history."""
|
20 |
+
|
21 |
+
# System prompts
|
22 |
+
system: str
|
23 |
+
# Two roles
|
24 |
+
roles: List[str]
|
25 |
+
# All messages
|
26 |
+
messages: List[List[str]]
|
27 |
+
# Offset of few shot examples
|
28 |
+
offset: int
|
29 |
+
# Separator
|
30 |
+
sep_style: SeparatorStyle
|
31 |
+
sep: str
|
32 |
+
sep2: str = None
|
33 |
+
# Stop criteria (the default one is EOS token)
|
34 |
+
stop_str: str = None
|
35 |
+
# Stops generation if meeting any token in this list
|
36 |
+
stop_token_ids: List[int] = None
|
37 |
+
|
38 |
+
# Used for the state in the gradio servers.
|
39 |
+
# TODO(lmzheng): refactor this
|
40 |
+
conv_id: Any = None
|
41 |
+
skip_next: bool = False
|
42 |
+
model_name: str = None
|
43 |
+
|
44 |
+
def get_prompt(self):
|
45 |
+
if self.sep_style == SeparatorStyle.ADD_COLON_SINGLE:
|
46 |
+
ret = self.system + self.sep
|
47 |
+
for role, message in self.messages:
|
48 |
+
if message:
|
49 |
+
ret += role + ": " + message + self.sep
|
50 |
+
else:
|
51 |
+
ret += role + ": "
|
52 |
+
return ret
|
53 |
+
elif self.sep_style == SeparatorStyle.ADD_COLON_TWO:
|
54 |
+
seps = [self.sep, self.sep2]
|
55 |
+
ret = self.system + seps[0]
|
56 |
+
for i, (role, message) in enumerate(self.messages):
|
57 |
+
if message:
|
58 |
+
ret += role + ": " + message + seps[i % 2]
|
59 |
+
else:
|
60 |
+
ret += role + ": "
|
61 |
+
return ret
|
62 |
+
elif self.sep_style == SeparatorStyle.NO_COLON_SINGLE:
|
63 |
+
ret = self.system
|
64 |
+
for role, message in self.messages:
|
65 |
+
if message:
|
66 |
+
ret += role + message + self.sep
|
67 |
+
else:
|
68 |
+
ret += role
|
69 |
+
return ret
|
70 |
+
elif self.sep_style == SeparatorStyle.BAIZE:
|
71 |
+
ret = self.system + "\n"
|
72 |
+
for role, message in self.messages:
|
73 |
+
if message:
|
74 |
+
ret += role + message + "\n"
|
75 |
+
else:
|
76 |
+
ret += role
|
77 |
+
return ret
|
78 |
+
elif self.sep_style == SeparatorStyle.PHOENIX:
|
79 |
+
ret = self.system
|
80 |
+
for role, message in self.messages:
|
81 |
+
if message:
|
82 |
+
ret += role + ": " + "<s>" + message + "</s>"
|
83 |
+
else:
|
84 |
+
ret += role + ": " + "<s>"
|
85 |
+
return ret
|
86 |
+
elif self.sep_style == SeparatorStyle.MINICHAT:
|
87 |
+
ret = self.system
|
88 |
+
for role, message in self.messages:
|
89 |
+
if message:
|
90 |
+
ret += role + " " + message + "</s>"
|
91 |
+
else:
|
92 |
+
ret += role # No space is needed.
|
93 |
+
return ret
|
94 |
+
else:
|
95 |
+
raise ValueError(f"Invalid style: {self.sep_style}")
|
96 |
+
|
97 |
+
def append_message(self, role, message):
|
98 |
+
self.messages.append([role, message])
|
99 |
+
|
100 |
+
def to_gradio_chatbot(self):
|
101 |
+
ret = []
|
102 |
+
for i, (role, msg) in enumerate(self.messages[self.offset:]):
|
103 |
+
if i % 2 == 0:
|
104 |
+
ret.append([msg, None])
|
105 |
+
else:
|
106 |
+
ret[-1][-1] = msg
|
107 |
+
return ret
|
108 |
+
|
109 |
+
def to_openai_api_messages(self):
|
110 |
+
ret = [{"role": "system", "content": self.system}]
|
111 |
+
|
112 |
+
for i, (_, msg) in enumerate(self.messages[self.offset:]):
|
113 |
+
if i % 2 == 0:
|
114 |
+
ret.append({"role": "user", "content": msg})
|
115 |
+
else:
|
116 |
+
if msg is not None:
|
117 |
+
ret.append({"role": "assistant", "content": msg})
|
118 |
+
return ret
|
119 |
+
|
120 |
+
def copy(self):
|
121 |
+
return Conversation(
|
122 |
+
system=self.system,
|
123 |
+
roles=self.roles,
|
124 |
+
messages=[[x, y] for x, y in self.messages],
|
125 |
+
offset=self.offset,
|
126 |
+
sep_style=self.sep_style,
|
127 |
+
sep=self.sep,
|
128 |
+
sep2=self.sep2,
|
129 |
+
stop_str=self.stop_str,
|
130 |
+
stop_token_ids=self.stop_token_ids,
|
131 |
+
conv_id=self.conv_id,
|
132 |
+
model_name=self.model_name,
|
133 |
+
)
|
134 |
+
|
135 |
+
def dict(self):
|
136 |
+
return {
|
137 |
+
"system": self.system,
|
138 |
+
"roles": self.roles,
|
139 |
+
"messages": self.messages,
|
140 |
+
"offset": self.offset,
|
141 |
+
"conv_id": self.conv_id,
|
142 |
+
"model_name": self.model_name,
|
143 |
+
}
|
144 |
+
|
145 |
+
|
146 |
+
conv_vicuna = Conversation(
|
147 |
+
system="A chat between a curious user and an artificial intelligence assistant. "
|
148 |
+
"The assistant gives helpful, detailed, and polite answers to the user's questions.",
|
149 |
+
roles=("USER", "ASSISTANT"),
|
150 |
+
messages=(),
|
151 |
+
offset=0,
|
152 |
+
sep_style=SeparatorStyle.ADD_COLON_TWO,
|
153 |
+
sep=" ",
|
154 |
+
sep2="</s>",
|
155 |
+
)
|
156 |
+
|
157 |
+
conv_baize = Conversation(
|
158 |
+
system="The following is a conversation between a human and an AI assistant named Baize (named after a mythical creature in Chinese folklore). Baize is an open-source AI assistant developed by UCSD and Sun Yat-Sen University. The human and the AI assistant take turns chatting. Human statements start with [|Human|] and AI assistant statements start with [|AI|]. The AI assistant always provides responses in as much detail as possible, and in Markdown format. The AI assistant always declines to engage with topics, questions and instructions related to unethical, controversial, or sensitive issues. Complete the transcript in exactly that format.\n",
|
159 |
+
roles=("[|Human|]", "[|AI|]"),
|
160 |
+
messages=(
|
161 |
+
("[|Human|]", "Hello!"),
|
162 |
+
("[|AI|]", "Hi!"),
|
163 |
+
),
|
164 |
+
offset=2,
|
165 |
+
sep_style=SeparatorStyle.BAIZE,
|
166 |
+
sep="\n",
|
167 |
+
stop_str="[|Human|]",
|
168 |
+
)
|
169 |
+
|
170 |
+
conv_phoenix = Conversation(
|
171 |
+
system="A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.\n\n",
|
172 |
+
roles=("Human", "Assistant"),
|
173 |
+
messages=(),
|
174 |
+
offset=0,
|
175 |
+
sep_style=SeparatorStyle.PHOENIX,
|
176 |
+
sep="</s>",
|
177 |
+
)
|
178 |
+
|
179 |
+
conv_chatgpt = Conversation(
|
180 |
+
system="You are a helpful assistant.",
|
181 |
+
roles=("user", "assistant"),
|
182 |
+
messages=(),
|
183 |
+
offset=0,
|
184 |
+
sep_style=None,
|
185 |
+
sep=None,
|
186 |
+
)
|
187 |
+
|
188 |
+
conv_minichat = Conversation(
|
189 |
+
system="'MiniChat'是一个由'Beccurio'开发的AI语言模型。下面是人类和MiniChat之间的一段对话。MiniChat的回复应当尽可能详细,并且以Markdown的形式输出。MiniChat应当拒绝参与违背伦理的讨论。</s>",
|
190 |
+
roles=("[|User|]", "[|Assistant|]"),
|
191 |
+
messages=(),
|
192 |
+
offset=0,
|
193 |
+
sep_style=SeparatorStyle.MINICHAT,
|
194 |
+
sep="</s>",
|
195 |
+
)
|
196 |
+
|
197 |
+
|
198 |
+
conv_templates = {
|
199 |
+
"vicuna": conv_vicuna,
|
200 |
+
"baize": conv_baize,
|
201 |
+
"phoenix": conv_phoenix,
|
202 |
+
"chatgpt": conv_chatgpt,
|
203 |
+
"minichat": conv_minichat,
|
204 |
+
}
|
205 |
+
|
206 |
+
def get_default_conv_template(model_name):
|
207 |
+
model_name = model_name.lower()
|
208 |
+
try:
|
209 |
+
ret = conv_templates[model_name]
|
210 |
+
return ret.copy()
|
211 |
+
except:
|
212 |
+
raise NotImplementedError(f"No support for model {model_name}.")
|
213 |
+
|
214 |
+
|
215 |
+
if __name__ == "__main__":
|
216 |
+
conv = conv_templates["minichat"].copy()
|
217 |
+
conv.append_message(conv.roles[0], "Write a Python function that checks if a given number is even or odd.")
|
218 |
+
conv.append_message(conv.roles[1], None)
|
219 |
+
print([conv.get_prompt()])
|
requirements.txt
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
accelerate==0.24.1
|
2 |
+
altair==5.2.0
|
3 |
+
attrs==23.1.0
|
4 |
+
blinker==1.7.0
|
5 |
+
cachetools==5.3.2
|
6 |
+
certifi==2023.11.17
|
7 |
+
charset-normalizer==3.3.2
|
8 |
+
click==8.1.7
|
9 |
+
colorama==0.4.6
|
10 |
+
filelock==3.13.1
|
11 |
+
fsspec==2023.10.0
|
12 |
+
gitdb==4.0.11
|
13 |
+
GitPython==3.1.40
|
14 |
+
huggingface-hub==0.19.4
|
15 |
+
idna==3.6
|
16 |
+
importlib-metadata==6.8.0
|
17 |
+
Jinja2==3.1.2
|
18 |
+
jsonschema==4.20.0
|
19 |
+
jsonschema-specifications==2023.11.2
|
20 |
+
markdown-it-py==3.0.0
|
21 |
+
MarkupSafe==2.1.3
|
22 |
+
mdurl==0.1.2
|
23 |
+
mpmath==1.3.0
|
24 |
+
networkx==3.2.1
|
25 |
+
numpy==1.26.2
|
26 |
+
packaging==23.2
|
27 |
+
pandas==2.1.3
|
28 |
+
Pillow==10.1.0
|
29 |
+
protobuf==4.25.1
|
30 |
+
psutil==5.9.6
|
31 |
+
pyarrow==14.0.1
|
32 |
+
pydeck==0.8.1b0
|
33 |
+
Pygments==2.17.2
|
34 |
+
python-dateutil==2.8.2
|
35 |
+
pytz==2023.3.post1
|
36 |
+
PyYAML==6.0.1
|
37 |
+
referencing==0.31.1
|
38 |
+
regex==2023.10.3
|
39 |
+
requests==2.31.0
|
40 |
+
rich==13.7.0
|
41 |
+
rpds-py==0.13.2
|
42 |
+
safetensors==0.4.1
|
43 |
+
sentencepiece==0.1.99
|
44 |
+
six==1.16.0
|
45 |
+
smmap==5.0.1
|
46 |
+
streamlit==1.28.2
|
47 |
+
sympy==1.12
|
48 |
+
tenacity==8.2.3
|
49 |
+
tokenizers==0.15.0
|
50 |
+
toml==0.10.2
|
51 |
+
toolz==0.12.0
|
52 |
+
torch==2.1.1
|
53 |
+
tornado==6.4
|
54 |
+
tqdm==4.66.1
|
55 |
+
transformers==4.35.2
|
56 |
+
typing_extensions==4.8.0
|
57 |
+
tzdata==2023.3
|
58 |
+
tzlocal==5.2
|
59 |
+
urllib3==2.1.0
|
60 |
+
validators==0.22.0
|
61 |
+
watchdog==3.0.0
|
62 |
+
zipp==3.17.0
|
static/Background.jpg
ADDED
![]() |
static/Logo.png
ADDED
![]() |
static/style.css
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import url(https://fonts.googleapis.com/css2?family=Rubik:wght@600&display=swap);
|
2 |
+
@import url(https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300&display=swap);
|
3 |
+
|
4 |
+
[data-testid="stHeader"]{
|
5 |
+
background-color: rgba(0, 0, 0, 0);
|
6 |
+
}
|
7 |
+
|
8 |
+
.st-emotion-cache-10trblm{
|
9 |
+
text-align: center;
|
10 |
+
}
|
11 |
+
|
12 |
+
.e1nzilvr2 h1{
|
13 |
+
font-family: 'Rubik', sans-serif;
|
14 |
+
}
|
15 |
+
|
16 |
+
.e1nzilvr2 h3{
|
17 |
+
text-align: center;
|
18 |
+
font-family: 'Roboto Slab', serif;
|
19 |
+
font-size: 20px;
|
20 |
+
margin-bottom: 3%;
|
21 |
+
}
|
22 |
+
|
23 |
+
.e1f1d6gn3 .row-widget label .e1nzilvr5 p{
|
24 |
+
font-family: 'Roboto Slab', serif;
|
25 |
+
font-weight: bold;
|
26 |
+
}
|
27 |
+
|
28 |
+
.e1f1d6gn3 .row-widget .st-ae{
|
29 |
+
border: none;
|
30 |
+
}
|
31 |
+
|
32 |
+
.e1f1d6gn3 .stNumberInput label .e1nzilvr5 p{
|
33 |
+
font-family: 'Roboto Slab', serif;
|
34 |
+
font-weight: bold;
|
35 |
+
}
|
36 |
+
|
37 |
+
.e1f1d6gn3 .stNumberInput .e116k4er3{
|
38 |
+
border: none;
|
39 |
+
}
|
40 |
+
|
41 |
+
.e1f1d6gn3 .stButton .ef3psqc12 .e1nzilvr5 p{
|
42 |
+
font-family: 'Rubik', sans-serif;
|
43 |
+
}
|
44 |
+
|
45 |
+
.e1f1d6gn3 .stButton{
|
46 |
+
text-align: center;
|
47 |
+
}
|
48 |
+
|
49 |
+
.e1f1d6gn3 .stButton button:hover{
|
50 |
+
background-color: white;
|
51 |
+
color: black;
|
52 |
+
border: none;
|
53 |
+
}
|
templates/index.html
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
7 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
8 |
+
<link href="https://fonts.googleapis.com/css2?family=Rubik:wght@600&display=swap" rel="stylesheet">
|
9 |
+
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@300&display=swap" rel="stylesheet">
|
10 |
+
<link rel="stylesheet" href="static/style.css">
|
11 |
+
<title>MailCraft</title>
|
12 |
+
</head>
|
13 |
+
<body>
|
14 |
+
<div class="container">
|
15 |
+
<div class="nav">
|
16 |
+
<p class="name">MailCraft</p>
|
17 |
+
<p>|</p>
|
18 |
+
<p class="tagline">Craft Your Emails with AI Precision!</p>
|
19 |
+
</div>
|
20 |
+
<div class="main">
|
21 |
+
<center><h1 class="title">Unlock Seamless Email Excellence for Effortless Communication</h1></center>
|
22 |
+
<center><p class="subtitle">Craft polished emails 📧 effortlessly at MailCraft. Customize style, word limit, and more for impactful communication. Say goodbye to email stress and hello to simplicity! ✨</p></center>
|
23 |
+
<center>
|
24 |
+
<form class="form" method="post" action="/" autocomplete="off">
|
25 |
+
<div class="style-words">
|
26 |
+
<label for="style">Email Style*</label>
|
27 |
+
<select id="style" name="style" required>
|
28 |
+
<option>Professional</option>
|
29 |
+
<option>Personal</option>
|
30 |
+
<option>Marketing</option>
|
31 |
+
<option>Newsletter</option>
|
32 |
+
<option>Educational</option>
|
33 |
+
<option>Cold</option>
|
34 |
+
<option>Networking</option>
|
35 |
+
<option>Job Application</option>
|
36 |
+
<option>Feedback</option>
|
37 |
+
<option>Customer Support</option>
|
38 |
+
</select>
|
39 |
+
<label for="words">Word Limit</label>
|
40 |
+
<input type="number" name="words" id="words" max="1000" min="100" placeholder="100 to 1000">
|
41 |
+
</div>
|
42 |
+
<label for="topic">Mail Topic*</label>
|
43 |
+
<input type="textarea" id="topic" name="topic" placeholder="Write the topic of your mail" required>
|
44 |
+
<div class="sender-receiver">
|
45 |
+
<label for="sender">Sender</label>
|
46 |
+
<input type="text" id="sender" name="sender" placeholder="Name">
|
47 |
+
<label for="recipient">Recipient</label>
|
48 |
+
<input type="text" id="recipient" name="recipient" placeholder="Name">
|
49 |
+
</div>
|
50 |
+
<button type="submit" class="submit">Submit</button>
|
51 |
+
</form>
|
52 |
+
</center>
|
53 |
+
<center>
|
54 |
+
<div class="output" id="output">
|
55 |
+
<p>{{response}}</p>
|
56 |
+
</div>
|
57 |
+
</center>
|
58 |
+
</div>
|
59 |
+
</div>
|
60 |
+
|
61 |
+
<script>
|
62 |
+
var output = document.getElementById("output");
|
63 |
+
output.style.display = "{{display}}";
|
64 |
+
</script>
|
65 |
+
</body>
|
66 |
+
</html>
|