harshSethi commited on
Commit
ec75db4
·
1 Parent(s): 666b025

add changes

Browse files
Files changed (6) hide show
  1. .gitignore +175 -0
  2. LICENSE +21 -0
  3. app.py +414 -0
  4. email_stats.json +4 -0
  5. requirements.txt +10 -0
  6. tools.py +262 -0
.gitignore ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
163
+
164
+ # dotenv environment variable file
165
+ .env
166
+
167
+ #google docs api
168
+ credentials.json
169
+
170
+ check_email.py
171
+ email_sender.py
172
+ google_docs.py
173
+ notion_connect.py
174
+
175
+
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2024 harshSethi11
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
app.py ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ import datetime as DT
4
+ import pytz
5
+ import time
6
+ import json
7
+ import re
8
+ import random
9
+ import string
10
+ from transformers import AutoTokenizer
11
+ from tools import toolsInfo
12
+
13
+ from dotenv import load_dotenv
14
+ load_dotenv()
15
+
16
+
17
+ from groq import Groq
18
+ client = Groq(
19
+ api_key=os.environ.get("GROQ_API_KEY"),
20
+ )
21
+ model_def = "llama-3.1-70b-versatile"
22
+ toolsModel = "llama3-groq-70b-8192-tool-use-preview"
23
+ maxTokens = 8000
24
+ tokenizer = AutoTokenizer.from_pretrained("Xenova/Meta-Llama-3.1-Tokenizer")
25
+
26
+ def countTokens(text):
27
+ text = str(text)
28
+ tokens = tokenizer.encode(text, add_special_tokens=False)
29
+ return len(tokens)
30
+
31
+ SYSTEM_MSG = """
32
+ You are an intelligent, personalized email generator for cold outreach, guiding the user through a structured workflow.
33
+ Ensure clarity and conciseness in your communication. Ask only one question at a time. you dont need to write the word "step" while asking the question, these are for you to understand the flow
34
+
35
+ # Step 1: Determine the Purpose of the Email
36
+ - Prompt the user to select the purpose of the email, using well-formatted numbered options.
37
+ - Group the options by category (Acquiring Customers, Learning and Connecting, Jobs and Hiring).
38
+ - Provide only numbers for the options, not the categories.
39
+ - Include a line break after each category name.
40
+
41
+ Example:
42
+ ##### Category: Acquiring Customers
43
+ 1. Lead Generation (spark interest in possible customers)
44
+ 2. Sales Outreach (directly contact decision-makers)
45
+ 3. Partnership and Collaboration (build mutually beneficial relationships)
46
+ 4. Event Promotion (invite people to webinars, conferences, or other events)
47
+ 5. Case Study or Testimonial Requests (ask satisfied customers for testimonials)
48
+
49
+ ##### Category: Learning and Connecting
50
+ 6. Networking (establish connections with industry experts)
51
+ 7. Market Research (gather information about target audiences or industries)
52
+ 8. Career Advice (seek guidance from experienced professionals)
53
+
54
+ ##### Category: Jobs and Hiring
55
+ 9. Job Application (apply for job openings)
56
+ 10. Job Referrals (ask for referrals or recommendations)
57
+ 11. Recruitment (reach out to potential candidates)
58
+
59
+ - now, you have to collect details, make sure you behave in a smart way, if the user mentions before suppose the recipient is a hotel manager, just confirm if the industry of the recipeint is hospitality, dont behave like dumb and ask the recipients's industry from the user. similarly for other parameters as well
60
+
61
+ # Step 2: Collect Sender Details
62
+ - Ask the user for the following specific information:
63
+ 1. **Objective**: Clarify the user's overall goal or purpose for sending the email (e.g., lead generation, networking, job application).
64
+ 2. **Personal Details**: Collect the sender's name, role, and company information (if applicable).
65
+ 3. **Industry**: Ask for the industry the sender works in to ensure relevance in the email content.
66
+
67
+ Example:
68
+ - "Please provide the objective for sending this email (e.g., Lead Generation, Networking)."
69
+ - "What are your personal details? Please provide your name, role, and company (if applicable)."
70
+ - "What industry are you in?"
71
+
72
+ # Step 3: Collect Recipient Information
73
+ - Ask the user for the following recipient-related details:
74
+ 1. **Recipient Name**: Collect the recipient's name to personalize the email.
75
+ 2. **Recipient Role**: Ask for the recipient's role in their company to tailor the email accordingly.
76
+ 3. **Recipient Industry**: Confirm or inquire about the recipient's industry (if different from the sender's).
77
+
78
+ Example:
79
+ - "What is the name of the recipient you are sending this email to?"
80
+ - "What is the recipient's role in their company?"
81
+ - "What industry does the recipient work in?"
82
+
83
+ - very important: Collect any other specific details that would help personalize or customize the email based on the user’s objective.
84
+ For example, you might ask if the user has interacted with the recipient before or if they know specific pain points to address in the email.
85
+
86
+ - now that you have collected these details, now you have to execute step 4 first that is save info in google sheets and then only move forward
87
+ # Step 4: Save Information to Google Sheets
88
+ - Save the collected sender and recipient details in a Google Sheet for easy reference.
89
+ - Verify with the user whether they can view the information in the sheet.
90
+
91
+ # Step 5: Draft Two Email Variations
92
+ - Based on the collected details, generate **two distinct variations** of the email.
93
+ - Ensure that each email is well-structured
94
+ - Present the two variations with numbered options for easy selection by the user.
95
+ - now that you know the industry of recipient, try to highlight the pain points of people in that industry, try to generate trust
96
+ - Example structure for email body: (you dont have to display these steps, like introduction etc., you just have to give the variation of the mail according to these steps)
97
+ -----
98
+ pargraph 1:Introduction
99
+ -----
100
+ paragraph 2: Key Message
101
+ -----
102
+ paragraph 3: Call to Action
103
+ -----
104
+ - while printing the template, it should resemble how the actual email looks like
105
+ - Ask the user which version they prefer and whether they would like to finalize it. If not, continue refining the email based on their feedback.
106
+ - think again if you have asked the user if he/she wants to add some more details
107
+ - once the user agrees, save that selected template ((important: the actual selected email, not the variation number)) in the Google Sheet.
108
+
109
+ # Step 6: Finalize the Email
110
+ - ((very important: now that you have got the selected template, you need to add some more sentence in qualiity english as per the details entered to the mail to generate trust withinh the recipient))
111
+ - Once the user selects an email version, ask for any missing placeholder values (e.g. user's phone number)
112
+ - Incorporate the placeholder values into the final email and show the user the complete draft.
113
+ - Confirm with the user whether the final email looks good or if additional changes are needed,
114
+
115
+ # Step 7: Send the Email
116
+ - Once the user approves the final version, ask for the recipient's exact email ID.
117
+ - dont assume recipients email id on the basis of the information entered by the user, explicity ask the email to which this message has to be sent
118
+ - Do **not** send the email until a valid email ID has been provided.
119
+ - important: ((the email has to be well structured like it was in the template, with proper paragraphs, dont just combine all the info into one paragraph into one and send it. also in the sent email in can see newline characters, this is send through api, so make sure that the send email, doesnt have these charcters and is complete i.e exactly what user selected00
120
+ - think again if you have followed the above instruction in step 7, if not correct them.
121
+ - Upon receiving a valid email, send the final formatted email.
122
+
123
+ # Step 8: congratulate the user
124
+ - After successfully sending the email, congratulate the user
125
+
126
+ # Step 9: Repeat Process
127
+ - Offer the user the option to repeat the process for additional profiles or recipients.
128
+ """
129
+
130
+ StartMsg = "HEY THERE!😊"
131
+
132
+
133
+ st.set_page_config(
134
+ page_title= "EmailGenie",
135
+ page_icon= "🧞‍♂️")
136
+
137
+ ipAddress = st.context.headers.get("x-forwarded-for")
138
+
139
+
140
+ def __nowInIST() -> DT.datetime:
141
+ return DT.datetime.now(pytz.timezone("Asia/Kolkata"))
142
+
143
+
144
+ def pprint(log: str):
145
+ now = __nowInIST()
146
+ now = now.strftime("%Y-%m-%d %H:%M:%S")
147
+ print(f"[{now}] [{ipAddress}] {log}")
148
+
149
+
150
+ pprint("\n")
151
+
152
+ st.markdown(
153
+ """
154
+ <style>
155
+ @keyframes blinker {
156
+ 0% {
157
+ opacity: 1;
158
+ }
159
+ 50% {
160
+ opacity: 0.2;
161
+ }
162
+ 100% {
163
+ opacity: 1;
164
+ }
165
+ }
166
+ .blinking {
167
+ animation: blinker 3s ease-out infinite;
168
+ }
169
+ .code {
170
+ color: green;
171
+ border-radius: 3px;
172
+ padding: 2px 4px; /* Padding around the text */
173
+ font-family: 'Courier New', Courier, monospace; /* Monospace font */
174
+ }
175
+ div[aria-label="dialog"] {
176
+ width: 90vw;
177
+ }
178
+ </style>
179
+ """,
180
+ unsafe_allow_html=True
181
+ )
182
+
183
+
184
+ def isInvalidResponse(response: str):
185
+ # new line followed by small case char
186
+ if len(re.findall(r'\n[a-z]', response)) > 3:
187
+ return True
188
+
189
+ # lot of repeating words
190
+ if len(re.findall(r'\b(\w+)(\s+\1){2,}\b', response)) > 1:
191
+ return True
192
+
193
+ # lots of paragraphs
194
+ if len(re.findall(r'\n\n', response)) > 25:
195
+ return True
196
+
197
+ def resetButtonState():
198
+ st.session_state["buttonValue"] = ""
199
+
200
+
201
+ def setStartMsg(msg):
202
+ st.session_state.startMsg = msg
203
+
204
+
205
+ if "chatHistory" not in st.session_state:
206
+ st.session_state.chatHistory = []
207
+
208
+ if "messages" not in st.session_state:
209
+ st.session_state.messages = []
210
+
211
+ if "buttonValue" not in st.session_state:
212
+ resetButtonState()
213
+
214
+ if "startMsg" not in st.session_state:
215
+ st.session_state.startMsg = ""
216
+
217
+ if "emailSent" not in st.session_state:
218
+ st.session_state.emailSent = False
219
+
220
+ st.session_state.toolResponseDisplay = {}
221
+
222
+
223
+ def getMessages():
224
+ def getContextSize():
225
+ currContextSize = countTokens(SYSTEM_MSG) + countTokens(st.session_state.messages) + 150
226
+ pprint(f"{currContextSize=}")
227
+ return currContextSize
228
+
229
+ while getContextSize() > maxTokens:
230
+ pprint("Context size exceeded, removing first message")
231
+ st.session_state.messages.pop(0)
232
+
233
+ return st.session_state.messages
234
+
235
+ tools = [
236
+ toolsInfo["customerDetailsGsheets"]["schema"],
237
+ toolsInfo["saveTemplate"]["schema"],
238
+ toolsInfo["sendEmail"]["schema"],
239
+ ]
240
+
241
+ def showToolResponse(toolResponseDisplay : dict):
242
+ # Directly display the message without any icon
243
+ st.markdown(toolResponseDisplay.get("text"))
244
+
245
+ def addToolCallToMsgs(toolCall: dict):
246
+ st.session_state.messages.append(
247
+ {
248
+ "role": "assistant",
249
+ "tool_calls": [
250
+ {
251
+ "id": toolCall.id,
252
+ "function": {
253
+ "name": toolCall.function.name,
254
+ "arguments": toolCall.function.arguments,
255
+ },
256
+ "type": toolCall.type,
257
+ }
258
+ ],
259
+ }
260
+ )
261
+
262
+ def processToolCalls(toolCalls):
263
+ for toolCall in toolCalls:
264
+ functionName = toolCall.function.name
265
+ functionToCall = toolsInfo[functionName]["func"]
266
+ functionArgsStr = toolCall.function.arguments
267
+ pprint(f"{functionName=} | {functionArgsStr=}")
268
+ functionArgs = json.loads(functionArgsStr)
269
+ functionResult = functionToCall(**functionArgs)
270
+ functionResponse = functionResult.get("response")
271
+ responseDisplay = functionResult.get("display")
272
+ pprint(f"{functionResponse=}")
273
+
274
+ if responseDisplay:
275
+ showToolResponse(responseDisplay)
276
+ st.session_state.toolResponseDisplay = responseDisplay
277
+
278
+ addToolCallToMsgs(toolCall)
279
+ st.session_state.messages.append(
280
+ {
281
+ "role": "tool",
282
+ "tool_call_id": toolCall.id,
283
+ "name": functionName,
284
+ "content": functionResponse,
285
+ }
286
+ )
287
+
288
+
289
+ def dedupeToolCalls(toolCalls: list):
290
+ toolCallsDict = {}
291
+ for toolCall in toolCalls:
292
+ toolCallsDict[toolCall.function.name] = toolCall
293
+ dedupedToolCalls = list(toolCallsDict.values())
294
+
295
+ if len(toolCalls) != len(dedupedToolCalls):
296
+ pprint("Deduped tool calls!")
297
+ pprint(f"{toolCalls=} -> {dedupedToolCalls=}")
298
+
299
+ return dedupedToolCalls
300
+
301
+
302
+ def getRandomToolId():
303
+ return ''.join(
304
+ random.choices(
305
+ string.ascii_lowercase + string.digits,
306
+ k=4
307
+ )
308
+ )
309
+
310
+ def predict(model: str = None):
311
+ MODEL = model or model_def
312
+
313
+ messagesFormatted = [{"role": "system", "content": SYSTEM_MSG}]
314
+ messagesFormatted.extend(getMessages())
315
+ contextSize = countTokens(messagesFormatted)
316
+ pprint(f"{contextSize=} | {MODEL}")
317
+ pprint(f"{messagesFormatted=}")
318
+
319
+ response = client.chat.completions.create(
320
+ model=MODEL,
321
+ messages=messagesFormatted,
322
+ temperature=0.5,
323
+ max_tokens=4000,
324
+ stream=False,
325
+ tools=tools
326
+ )
327
+
328
+ responseMessage = response.choices[0].message
329
+ # pprint(f"{responseMessage=}")
330
+ responseContent = responseMessage.content
331
+ # pprint(f"{responseContent=}")
332
+
333
+ if responseContent and '<function=' in responseContent:
334
+ pprint("Switching to TOOLS_MODEL")
335
+ return predict(toolsModel)
336
+
337
+
338
+ if responseContent:
339
+ yield responseContent
340
+ toolCalls = responseMessage.tool_calls
341
+ # pprint(f"{toolCalls=}")
342
+
343
+ if toolCalls:
344
+ pprint(f"{toolCalls=}")
345
+ toolCalls = dedupeToolCalls(toolCalls)
346
+ try:
347
+ processToolCalls(toolCalls)
348
+ return predict()
349
+ except Exception as e:
350
+ pprint(e)
351
+
352
+ st.title("EmailGenie 📧🧞‍♂️")
353
+ if not (st.session_state["buttonValue"] or st.session_state["startMsg"]):
354
+ st.button(StartMsg, on_click=lambda: setStartMsg(StartMsg))
355
+ for chat in st.session_state.chatHistory:
356
+ role = chat["role"]
357
+ content = chat["content"]
358
+ toolResponseDisplay = chat.get("toolResponseDisplay")
359
+ with st.chat_message(role):
360
+ st.markdown(content)
361
+ if toolResponseDisplay:
362
+ showToolResponse(toolResponseDisplay)
363
+
364
+
365
+ if prompt := (st.chat_input() or st.session_state["buttonValue"] or st.session_state["startMsg"]):
366
+ resetButtonState()
367
+ setStartMsg("")
368
+
369
+ with st.chat_message("user"):
370
+ st.markdown(prompt)
371
+ pprint(f"{prompt=}")
372
+ st.session_state.chatHistory.append({"role": "user", "content": prompt })
373
+ st.session_state.messages.append({"role": "user", "content": prompt })
374
+
375
+ with st.chat_message("assistant"):
376
+ responseContainer = st.empty()
377
+
378
+ def __printAndGetResponse():
379
+ response = ""
380
+ # responseContainer.markdown(".....")
381
+ responseGenerator = predict()
382
+
383
+ for chunk in responseGenerator:
384
+ response += chunk
385
+ if isInvalidResponse(response):
386
+ pprint(f"Invalid_{response=}")
387
+ return
388
+ responseContainer.markdown(response)
389
+
390
+ return response
391
+
392
+ response = __printAndGetResponse()
393
+ while not response:
394
+ pprint("Empty response. Retrying..")
395
+ time.sleep(0.5)
396
+ response = __printAndGetResponse()
397
+
398
+ pprint(f"{response=}")
399
+
400
+ def selectButton(optionLabel):
401
+ st.session_state["buttonValue"] = optionLabel
402
+ pprint(f"Selected: {optionLabel}")
403
+
404
+ toolResponseDisplay = st.session_state.toolResponseDisplay
405
+ st.session_state.chatHistory.append({
406
+ "role": "assistant",
407
+ "content": response,
408
+ "toolResponseDisplay": toolResponseDisplay
409
+ })
410
+
411
+ st.session_state.messages.append({
412
+ "role": "assistant",
413
+ "content": response,
414
+ })
email_stats.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+
2
+ {"date": "2024-10-06T20:49:28.154835", "requests": 1, "delivered": 1}
3
+ {"date": "2024-10-06T20:51:22.622734", "requests": 1, "delivered": 1}
4
+ {"date": "2024-10-06T20:52:55.194269", "requests": 1, "delivered": 1}
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ pytz
3
+ transformers
4
+ python-dotenv
5
+ gspread
6
+ google-auth
7
+ notion-client
8
+ groq
9
+
10
+
tools.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gspread
2
+ import os
3
+ import json
4
+ import pandas
5
+ import streamlit as st
6
+ from google.oauth2.service_account import Credentials
7
+ from datetime import datetime
8
+
9
+ from dotenv import load_dotenv
10
+ load_dotenv()
11
+
12
+ app_pswd = os.environ.get("app_pswd")
13
+ sheet_id = os.environ.get("sheet_id")
14
+
15
+
16
+ email_add = "[email protected]"
17
+ my_name = "harsh sethi"
18
+ template_col_name = "template"
19
+
20
+ def customerDetailsGsheets( objective,personal_details,industry,recipientRole,recipientName):
21
+ scopes = [
22
+ "https://www.googleapis.com/auth/spreadsheets"
23
+ ]
24
+ creds = Credentials.from_service_account_file("credentials.json", scopes= scopes)
25
+ client = gspread.authorize(creds)
26
+ try:
27
+ workbook = client.open_by_key(sheet_id)
28
+ except gspread.SpreadsheetNotFound:
29
+ return {"response": "Spreadsheet not found.", "status": "error"}
30
+
31
+ # Select the specific worksheet (tab) where you want to append the data
32
+ try:
33
+ sheet = workbook.worksheet("Sheet1") # Add the worksheet name here
34
+ except gspread.WorksheetNotFound:
35
+ return {"response": "Worksheet not found.", "status": "error"}
36
+
37
+ # Append the customer details as a new row in the worksheet
38
+ if not (objective and personal_details):
39
+ return{
40
+ "response" : "please enter the important details"
41
+ }
42
+
43
+
44
+ try:
45
+ sheet.append_row([objective, personal_details, industry, recipientRole, recipientName])
46
+ return {
47
+ "response": "Saved customer details in Google Sheet.",
48
+ "display": {
49
+ "text": f"Customer details saved in Google Sheet: [Link]({workbook.url})"
50
+ },
51
+ "status": "success"
52
+ }
53
+ except Exception as e:
54
+ return {"response": f"Error saving data: {str(e)}", "status": "error"}
55
+
56
+
57
+ def saveTemplate(template:str):
58
+ scopes = [
59
+ "https://www.googleapis.com/auth/spreadsheets"
60
+ ]
61
+
62
+ # Authorize using the service account credentials
63
+ creds = Credentials.from_service_account_file("credentials.json", scopes=scopes)
64
+ client = gspread.authorize(creds)
65
+
66
+ # Open the workbook using the spreadsheet ID
67
+ workbook = client.open_by_key(sheet_id)
68
+
69
+ # Try to access the specific worksheet
70
+ try:
71
+ sheet = workbook.worksheet("Sheet1")
72
+ except gspread.WorksheetNotFound:
73
+ print(f"Worksheet 'Sheet1' not found.")
74
+ return {
75
+ "response": "Failed to save template. Worksheet not found.",
76
+ "display": {
77
+ "text": "Failed to save template. Worksheet not found."
78
+ },
79
+ "status": "error"
80
+ }
81
+
82
+ # Get the headers to locate the template column
83
+ headers = sheet.row_values(1)
84
+ try:
85
+ templateColIndex = headers.index(template_col_name) + 1 # Replace with your actual column name
86
+ except ValueError:
87
+ print("Template column not found in the headers.")
88
+ return {
89
+ "response": "Failed to save template. Template column not found.",
90
+ "display": {
91
+ "text": "Failed to save template. Template column not found."
92
+ },
93
+ "status": "error"
94
+ }
95
+
96
+ # Find the last available row to insert the template
97
+ lastRow = len([row for row in sheet.get_all_values() if any(row)])
98
+
99
+ # Update the cell with the template in the correct column
100
+ sheet.update_cell(lastRow, templateColIndex, template)
101
+
102
+ # Return success response
103
+ return {
104
+ "response": "Saved template in Google Sheet.",
105
+ "display": {
106
+ "text": f"Template saved successfully in Google Sheet: [Link]({workbook.url})"
107
+ },
108
+ "status": "success"
109
+ }
110
+
111
+
112
+ def sendEmail(recipient_mail,mail_body,mail_subject):
113
+ import smtplib
114
+ from email.mime.multipart import MIMEMultipart
115
+ from email.mime.text import MIMEText
116
+
117
+ # Your Gmail credentials
118
+ sender_email = email_add
119
+ password = app_pswd
120
+ receiver_email = recipient_mail
121
+
122
+ # Set up the server using Gmail's SMTP server
123
+ server = smtplib.SMTP('smtp.gmail.com', 587)
124
+ server.starttls() # Secure the connection
125
+ server.login(sender_email, password)
126
+
127
+ # Create email
128
+ msg = MIMEMultipart()
129
+ msg['From'] = sender_email
130
+ msg['To'] = receiver_email
131
+ msg['Subject'] = mail_subject
132
+
133
+ # Email body
134
+ body = mail_body
135
+ msg.attach(MIMEText(body, 'plain'))
136
+
137
+ # Send the email
138
+ try:
139
+ server.sendmail(sender_email, receiver_email, msg.as_string())
140
+ # Log email stats
141
+ email_status = {
142
+ 'date': datetime.now().isoformat(),
143
+ 'requests': 1,
144
+ 'delivered': 1 # Assuming delivery was successful
145
+ }
146
+ with open('email_stats.json', 'a') as f:
147
+ json.dump(email_status, f)
148
+ f.write('\n') # New line for each log entry
149
+
150
+ # Disconnect from the server
151
+ server.quit()
152
+
153
+ print("Email sent successfully!")
154
+ return {
155
+ "response": "Email sent",
156
+ "display": {
157
+ "text": f"Email sent to {recipient_mail}"
158
+ }
159
+ }
160
+ except Exception as e:
161
+ print(e.message)
162
+ server.quit()
163
+ return {
164
+ "response": f"Failed to send email. Error: {e.message}",
165
+ "display": {
166
+ "text": f"Failed to send email to {recipient_mail}"
167
+ }
168
+ }
169
+
170
+
171
+ toolsInfo = {
172
+ "customerDetailsGsheets": {
173
+ "func": customerDetailsGsheets,
174
+ "schema": {
175
+ "type": "function",
176
+ "function": {
177
+ "name": "customerDetailsGsheets",
178
+ "description": "Saves customer details in Google Sheets",
179
+ "parameters": {
180
+ "type": "object",
181
+ "properties": {
182
+ "objective": {
183
+ "type": "string",
184
+ "description": "Objective or purpose of the email"
185
+ },
186
+ "personal_details": {
187
+ "type": "string",
188
+ "description": "Personal details about the sender"
189
+ },
190
+ "industry": {
191
+ "type": "string",
192
+ "description": "Recipient's industry"
193
+ },
194
+ "recipientRole": {
195
+ "type": "string",
196
+ "description": "Recipient's role in the company"
197
+ },
198
+ "recipientName": {
199
+ "type": "string",
200
+ "description": "Name of the recipient"
201
+ }
202
+ },
203
+ "required": ["objective", "personal_details", "industry", "recipientRole", "recipientName"]
204
+ }
205
+ }
206
+ },
207
+ },
208
+
209
+ "saveTemplate": {
210
+ "func": saveTemplate,
211
+ "schema": {
212
+ "type": "function",
213
+ "function": {
214
+ "name": "saveTemplate",
215
+ "description": "Saves the email template in Google Sheets",
216
+ "parameters": {
217
+ "type": "object",
218
+ "properties": {
219
+ "template": {
220
+ "type": "string",
221
+ "description": "Email template to save"
222
+ }
223
+ },
224
+ "required": ["template"]
225
+ }
226
+ }
227
+ },
228
+ },
229
+
230
+ "sendEmail": {
231
+ "func": sendEmail,
232
+ "schema": {
233
+ "type": "function",
234
+ "function": {
235
+ "name": "sendEmail",
236
+ "description": "Sends an email to the recipient",
237
+ "parameters": {
238
+ "type": "object",
239
+ "properties": {
240
+ "recipient_mail": {
241
+ "type": "string",
242
+ "description": "Email address of the recipient"
243
+ },
244
+ "mail_body": {
245
+ "type": "string",
246
+ "description": "Body content of the email"
247
+ },
248
+ "mail_subject": {
249
+ "type": "string",
250
+ "description": "Subject of the email"
251
+ }
252
+ },
253
+ "required": ["recipient_mail", "mail_body", "mail_subject"]
254
+ }
255
+ }
256
+ },
257
+ },
258
+ }
259
+
260
+
261
+
262
+