Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -8,7 +8,6 @@ import email, imaplib, json, time
|
|
8 |
import torch, logging
|
9 |
import uvicorn
|
10 |
from pydantic import BaseModel
|
11 |
-
import pandas as pd
|
12 |
|
13 |
app = FastAPI()
|
14 |
|
@@ -17,14 +16,18 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(
|
|
17 |
logger = logging.getLogger(__name__)
|
18 |
|
19 |
# Email and database configuration
|
|
|
|
|
|
|
20 |
DB_CONFIG = {
|
21 |
'host': '0.tcp.in.ngrok.io',
|
22 |
-
'port':
|
23 |
'user': 'root',
|
24 |
-
'password': '',
|
25 |
'database': 'shipment_details'
|
26 |
-
|
27 |
|
|
|
28 |
output_format = {
|
29 |
"origin": "",
|
30 |
"destination": "",
|
@@ -34,25 +37,26 @@ output_format = {
|
|
34 |
"Description": "",
|
35 |
"Quantities": "",
|
36 |
"Carrier_details": ""
|
37 |
-
|
38 |
|
39 |
-
|
|
|
40 |
System prompt: You will be provided with an email containing shipment details. Your task is to extract specific information based on the given instructions.
|
41 |
|
42 |
Instructions:
|
43 |
-
1.
|
44 |
-
2.
|
45 |
-
3. Extract the following
|
46 |
-
- origin
|
47 |
-
- destination
|
48 |
-
- expected_shipment_datetime
|
49 |
-
- types_of_service
|
50 |
-
- warehouse
|
51 |
-
- description
|
52 |
-
- quantities
|
53 |
-
- carrier_details
|
54 |
-
4.
|
55 |
-
{
|
56 |
"origin": "",
|
57 |
"destination": "",
|
58 |
"expected_shipment_datetime": "",
|
@@ -61,68 +65,9 @@ Instructions:
|
|
61 |
"description": "",
|
62 |
"quantities": "",
|
63 |
"carrier_details": ""
|
64 |
-
}
|
65 |
-
Examples:
|
66 |
-
|
67 |
-
1. Email: We are pleased to inform you of an upcoming shipment originating from Hamburg and destined for New York. The shipment is expected to arrive on August 15, 2024. This consignment includes various electronics, with an estimated quantity of 200 units. The service type for this shipment is AIR, provided by our reliable carrier, Sky Logistics.
|
68 |
-
Extracted Information:
|
69 |
-
origin: Hamburg,
|
70 |
-
destination: New York,
|
71 |
-
expected_shipment_datetime: 2024-08-15 00:00:000,
|
72 |
-
types_of_service: AIR,
|
73 |
-
warehouse: Sky Logistics,
|
74 |
-
description: We are pleased to inform you of an upcoming shipment originating from Hamburg and destined for New York. The shipment is expected to arrive on August 15, 2024.,
|
75 |
-
quantities: 200 units,
|
76 |
-
carrier_details: Sky Logistics
|
77 |
-
|
78 |
-
2. Email: Please be advised of a shipment from our supplier in Shanghai heading to Los Angeles. The expected date of arrival is July 30, 2024. The shipment consists of mixed goods, mainly textiles, with a total of 500 pieces. This delivery will be handled through LCL service by Ocean Freight Co.
|
79 |
-
Extracted Information:
|
80 |
-
origin: Shanghai,
|
81 |
-
destination: Los Angeles,
|
82 |
-
expected_shipment_datetime: 2024-07-30 00:00:0000,
|
83 |
-
types_of_service: LCL,
|
84 |
-
warehouse: Ocean Freight Co.,
|
85 |
-
description: Please be advised of a shipment from our supplier in Shanghai heading to Los Angeles. The expected date of arrival is July 30, 2024.,
|
86 |
-
quantities: 500 pieces,
|
87 |
-
carrier_details: Ocean Freight Co.
|
88 |
-
|
89 |
-
3. Email: A new shipment is on its way from Mumbai to London, scheduled to reach by August 22, 2024. This batch contains furniture items, totaling 150 pieces. It is managed by Global Carriers.
|
90 |
-
Extracted Information:
|
91 |
-
origin: Mumbai,
|
92 |
-
destination: London,
|
93 |
-
expected_shipment_datetime: 2024-08-22 00:00:00000,
|
94 |
-
types_of_service: null,
|
95 |
-
warehouse: Global Carriers,
|
96 |
-
description: A new shipment is on its way from Mumbai to London, scheduled to reach by August 22, 2024.,
|
97 |
-
quantities: 150 pieces,
|
98 |
-
carrier_details: Global Carriers
|
99 |
-
|
100 |
-
4. Email: We are notifying you about a shipment dispatched from Tokyo, heading towards Sydney, with an estimated arrival date of September 10, 2024. The cargo includes automotive parts, summing up to 350 units. This shipment will be transported via AIR service, operated by Jet Logistics.
|
101 |
-
Extracted Information:
|
102 |
-
origin: Tokyo,
|
103 |
-
destination: Sydney,
|
104 |
-
expected_shipment_datetime: 2024-09-10 00:00:0000,
|
105 |
-
types_of_service: AIR,
|
106 |
-
warehouse: Jet Logistics,
|
107 |
-
description: We are notifying you about a shipment dispatched from Tokyo, heading towards Sydney, with an estimated arrival date of September 10, 2024.,
|
108 |
-
quantities: 350 units,
|
109 |
-
carrier_details: Jet Logistics
|
110 |
-
|
111 |
-
5. Email: Kindly note the details of a forthcoming shipment from Berlin to Toronto. The shipment encompasses various household goods, with a total quantity of 400 items. We have arranged for this to be shipped using LCL service, provided by Sea Wave Transport.
|
112 |
-
Extracted Information:
|
113 |
-
origin: Berlin,
|
114 |
-
destination: Toronto,
|
115 |
-
expected_shipment_datetime: null,
|
116 |
-
types_of_service: LCL,
|
117 |
-
warehouse: Sea Wave Transport,
|
118 |
-
description: Kindly note the details of a forthcoming shipment from Berlin to Toronto. The expected arrival is on August 5, 2024.,
|
119 |
-
quantities: 400 items,
|
120 |
-
carrier_details: Sea Wave Transport
|
121 |
-
|
122 |
-
Output: {output_format}
|
123 |
"""
|
124 |
|
125 |
-
|
126 |
# Function to insert extracted shipment details into MySQL database
|
127 |
def insert_data(extracted_details):
|
128 |
try:
|
@@ -172,50 +117,86 @@ def insert_data(extracted_details):
|
|
172 |
except Exception as ex:
|
173 |
logger.error(f"Error inserting data: {ex}")
|
174 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
# Function to read and process unread emails
|
176 |
def read_email():
|
177 |
logging.info('ready to read email ! ...')
|
178 |
try:
|
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 |
except Exception as e:
|
214 |
logger.error(f"Error reading emails: {e}")
|
215 |
|
216 |
-
#
|
217 |
running = False
|
218 |
-
loop_thread = None
|
219 |
|
220 |
# HTML content for the web interface
|
221 |
html_content = """
|
@@ -237,37 +218,28 @@ html_content = """
|
|
237 |
cursor: pointer;
|
238 |
}
|
239 |
button.stop { background-color: #f44336; }
|
240 |
-
#status { font-weight: bold; }
|
241 |
</style>
|
242 |
-
<script>
|
243 |
-
async function startLoop() {
|
244 |
-
const response = await fetch('/start', { method: 'POST' });
|
245 |
-
const result = await response.text();
|
246 |
-
document.getElementById("status").innerHTML = result;
|
247 |
-
}
|
248 |
-
|
249 |
-
async function stopLoop() {
|
250 |
-
const response = await fetch('/stop', { method: 'POST' });
|
251 |
-
const result = await response.text();
|
252 |
-
document.getElementById("status").innerHTML = result;
|
253 |
-
}
|
254 |
-
</script>
|
255 |
</head>
|
256 |
<body>
|
257 |
-
<h1>Email Processing Status:
|
258 |
-
<button onclick="startLoop()">Start</button>
|
259 |
-
<button class="stop" onclick="stopLoop()">Stop</button>
|
260 |
</body>
|
261 |
</html>
|
262 |
"""
|
263 |
|
|
|
|
|
|
|
|
|
|
|
|
|
264 |
# Function to process emails in a loop
|
265 |
def email_processing_loop():
|
266 |
global running
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
|
|
271 |
|
272 |
# Endpoint to display the current email processor status
|
273 |
@app.get("/", response_class=HTMLResponse)
|
@@ -275,33 +247,40 @@ async def home():
|
|
275 |
global running
|
276 |
status = "Running" if running else "Stopped"
|
277 |
return HTMLResponse(content=html_content.replace("{{ status }}", status), status_code=200)
|
278 |
-
|
279 |
-
# Endpoint to start the email processing loop
|
280 |
-
@app.post("/start")
|
281 |
-
async def start_email_loop():
|
282 |
global running, loop_thread
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
else:
|
290 |
-
|
|
|
|
|
|
|
291 |
|
292 |
-
# Endpoint to stop the email processing loop
|
293 |
-
@app.post("/stop")
|
294 |
-
async def stop_email_loop():
|
295 |
-
global running
|
296 |
-
if running:
|
297 |
-
running = False
|
298 |
-
logging.info("Email processing loop stopped.")
|
299 |
-
return "Stopped"
|
300 |
-
else:
|
301 |
-
return "Already stopped"
|
302 |
|
303 |
if __name__ == "__main__":
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
|
|
|
|
|
|
|
8 |
import torch, logging
|
9 |
import uvicorn
|
10 |
from pydantic import BaseModel
|
|
|
11 |
|
12 |
app = FastAPI()
|
13 |
|
|
|
16 |
logger = logging.getLogger(__name__)
|
17 |
|
18 |
# Email and database configuration
|
19 |
+
IMAP_SERVER = 'imap.gmail.com'
|
20 |
+
EMAIL_ADDRESS = '[email protected]'
|
21 |
+
PASSWORD = 'gclc wsnx kywt uvqy ' # Store this securely in production
|
22 |
DB_CONFIG = {
|
23 |
'host': '0.tcp.in.ngrok.io',
|
24 |
+
'port': 11329,
|
25 |
'user': 'root',
|
26 |
+
'password': '', # Add the correct password
|
27 |
'database': 'shipment_details'
|
28 |
+
}
|
29 |
|
30 |
+
# JSON format for extracted shipment details
|
31 |
output_format = {
|
32 |
"origin": "",
|
33 |
"destination": "",
|
|
|
37 |
"Description": "",
|
38 |
"Quantities": "",
|
39 |
"Carrier_details": ""
|
40 |
+
}
|
41 |
|
42 |
+
# Prompt for LLM to process shipment-related emails
|
43 |
+
prompt = """
|
44 |
System prompt: You will be provided with an email containing shipment details. Your task is to extract specific information based on the given instructions.
|
45 |
|
46 |
Instructions:
|
47 |
+
1. Focus only on extracting details about future shipments, ignore irrelevant information.
|
48 |
+
2. Output should be in JSON format. Missing information should be marked as null.
|
49 |
+
3. Extract the following:
|
50 |
+
- origin
|
51 |
+
- destination
|
52 |
+
- expected_shipment_datetime (format: yyyy-mm-dd hh:mm:ss)
|
53 |
+
- types_of_service (AIR, LCL, FCL)
|
54 |
+
- warehouse
|
55 |
+
- description (max 100 words)
|
56 |
+
- quantities
|
57 |
+
- carrier_details
|
58 |
+
4. The output should be formatted as follows:
|
59 |
+
{
|
60 |
"origin": "",
|
61 |
"destination": "",
|
62 |
"expected_shipment_datetime": "",
|
|
|
65 |
"description": "",
|
66 |
"quantities": "",
|
67 |
"carrier_details": ""
|
68 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
"""
|
70 |
|
|
|
71 |
# Function to insert extracted shipment details into MySQL database
|
72 |
def insert_data(extracted_details):
|
73 |
try:
|
|
|
117 |
except Exception as ex:
|
118 |
logger.error(f"Error inserting data: {ex}")
|
119 |
|
120 |
+
# Function to extract shipment details using an LLM
|
121 |
+
def get_details(mail):
|
122 |
+
try:
|
123 |
+
# Initialize LLM model and tokenizer
|
124 |
+
# Uncomment below if using Hugging Face models, or load your specific model accordingly
|
125 |
+
# pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
|
126 |
+
# output = pipe(f"{prompt}\n{mail}", max_new_tokens=200)
|
127 |
+
|
128 |
+
# Using Llama model for completion
|
129 |
+
llm = Llama(model_path="./ggml-model-q8_0.gguf", n_ctx=2048, n_batch=512)
|
130 |
+
response = llm.create_chat_completion(
|
131 |
+
messages=[
|
132 |
+
{"role": "system", "content": prompt},
|
133 |
+
{"role": "user", "content": mail}
|
134 |
+
],
|
135 |
+
max_tokens=200
|
136 |
+
)
|
137 |
+
return response['choices'][0]['message']['content']
|
138 |
+
|
139 |
+
except Exception as ex:
|
140 |
+
logger.error(f"Error generating details from LLM: {ex}")
|
141 |
+
return None
|
142 |
+
|
143 |
# Function to read and process unread emails
|
144 |
def read_email():
|
145 |
logging.info('ready to read email ! ...')
|
146 |
try:
|
147 |
+
logging.info('get imap server!')
|
148 |
+
mail = imaplib.IMAP4_SSL(IMAP_SERVER)
|
149 |
+
mail.login(EMAIL_ADDRESS, PASSWORD)
|
150 |
+
mail.select('inbox')
|
151 |
+
logging.info('select mail inbox')
|
152 |
+
status, messages = mail.search(None, 'UNSEEN')
|
153 |
+
message_ids = messages[0].split()
|
154 |
+
logging.info(f"Total unread emails: {len(message_ids)}")
|
155 |
+
print(f"Total unread emails: {len(message_ids)}")
|
156 |
+
|
157 |
+
for message_id in message_ids:
|
158 |
+
try:
|
159 |
+
status, data = mail.fetch(message_id, '(RFC822)')
|
160 |
+
raw_email = data[0][1]
|
161 |
+
email_message = email.message_from_bytes(raw_email)
|
162 |
+
|
163 |
+
# Extract metadata
|
164 |
+
sender = email_message['From']
|
165 |
+
receiver = email_message['To']
|
166 |
+
cc = email_message.get('Cc', '')
|
167 |
+
bcc = email_message.get('Bcc', '')
|
168 |
+
subject = email_message['Subject']
|
169 |
+
|
170 |
+
# Extract email body
|
171 |
+
if email_message.is_multipart():
|
172 |
+
for part in email_message.walk():
|
173 |
+
if part.get_content_type() == 'text/plain':
|
174 |
+
email_body = part.get_payload(decode=True).decode('utf-8')
|
175 |
+
break
|
176 |
+
else:
|
177 |
+
email_body = email_message.get_payload(decode=True).decode('utf-8')
|
178 |
+
|
179 |
+
# Extract and store details
|
180 |
+
extracted_details_str = get_details(email_body)
|
181 |
+
extracted_details = json.loads(extracted_details_str)
|
182 |
+
meta_data = {
|
183 |
+
'sender': sender, 'receiver': receiver, 'cc': cc, 'bcc': bcc, 'subject': subject
|
184 |
+
}
|
185 |
+
extracted_details.update(meta_data)
|
186 |
+
insert_data(extracted_details)
|
187 |
+
|
188 |
+
except Exception as e:
|
189 |
+
logger.error(f"Error processing email {message_id}: {e}")
|
190 |
+
|
191 |
+
mail.close()
|
192 |
+
mail.logout()
|
193 |
|
194 |
except Exception as e:
|
195 |
logger.error(f"Error reading emails: {e}")
|
196 |
|
197 |
+
# Email processing loop
|
198 |
running = False
|
199 |
+
loop_thread = None
|
200 |
|
201 |
# HTML content for the web interface
|
202 |
html_content = """
|
|
|
218 |
cursor: pointer;
|
219 |
}
|
220 |
button.stop { background-color: #f44336; }
|
|
|
221 |
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
</head>
|
223 |
<body>
|
224 |
+
<h1>Email Processing Status: {{ status }}</h1>
|
|
|
|
|
225 |
</body>
|
226 |
</html>
|
227 |
"""
|
228 |
|
229 |
+
class ActionModel(BaseModel):
|
230 |
+
action: str # 'start' or 'stop'
|
231 |
+
|
232 |
+
class ModelData(BaseModel):
|
233 |
+
data: str # Additional email-related information
|
234 |
+
|
235 |
# Function to process emails in a loop
|
236 |
def email_processing_loop():
|
237 |
global running
|
238 |
+
logger.info("Starting email processing loop...")
|
239 |
+
while running:
|
240 |
+
# read_email() # Assuming this is your email processing function
|
241 |
+
print("$"*100)
|
242 |
+
time.sleep(10) # Check for new emails every 10 seconds
|
243 |
|
244 |
# Endpoint to display the current email processor status
|
245 |
@app.get("/", response_class=HTMLResponse)
|
|
|
247 |
global running
|
248 |
status = "Running" if running else "Stopped"
|
249 |
return HTMLResponse(content=html_content.replace("{{ status }}", status), status_code=200)
|
250 |
+
async def control_email_loop(action: ActionModel, data: ModelData):
|
|
|
|
|
|
|
251 |
global running, loop_thread
|
252 |
+
logger.info(action.action)
|
253 |
+
|
254 |
+
if action.action == "start":
|
255 |
+
if not running:
|
256 |
+
running = True
|
257 |
+
email_data = data.data # This is already a string, no need to call .dict()
|
258 |
+
logger.info(f"Email Data: {email_data}")
|
259 |
+
loop_thread = threading.Thread(target=email_processing_loop, daemon=True)
|
260 |
+
loop_thread.start()
|
261 |
+
logger.info("Email processing loop started.")
|
262 |
+
else:
|
263 |
+
logger.info("Email processing loop is already running.")
|
264 |
+
|
265 |
+
elif action.action == "stop":
|
266 |
+
if running:
|
267 |
+
running = False
|
268 |
+
logger.info("Email processing loop stopped.")
|
269 |
+
else:
|
270 |
+
logger.info("Email processing loop is not running.")
|
271 |
+
|
272 |
else:
|
273 |
+
raise HTTPException(status_code=400, detail="Invalid action. Use 'start' or 'stop'.")
|
274 |
+
|
275 |
+
status = "Running" if running else "Stopped"
|
276 |
+
return HTMLResponse(content=html_content.replace("{{ status }}", status), status_code=200)
|
277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
|
279 |
if __name__ == "__main__":
|
280 |
+
|
281 |
+
print('starting')
|
282 |
+
logging.info('starting project!...')
|
283 |
+
# running = True
|
284 |
+
# threading.Thread(target=email_processing_loop, daemon=True).start()
|
285 |
+
logging.info('...')
|
286 |
+
uvicorn.run()
|