MBG0903 commited on
Commit
07881f8
Β·
verified Β·
1 Parent(s): a2ded6d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +191 -43
app.py CHANGED
@@ -1,6 +1,12 @@
 
 
 
 
 
1
  import os
2
  import re
3
  import random
 
4
  from datetime import datetime, timedelta
5
 
6
  import pandas as pd
@@ -85,7 +91,7 @@ def regenerate_db():
85
 
86
 
87
  # ----------------------------
88
- # Lightweight inquiry parsing (no LLM)
89
  # ----------------------------
90
  def normalize_text(t: str) -> str:
91
  return re.sub(r"\s+", " ", (t or "").strip().lower())
@@ -182,6 +188,9 @@ def pick_margin(pricing_mode: str, base_margin: float):
182
  return base_margin
183
 
184
 
 
 
 
185
  def compute_offers(req: dict, suppliers: pd.DataFrame, margin_pct: float):
186
  category = req.get("category")
187
  brand = req.get("brand")
@@ -207,14 +216,14 @@ def compute_offers(req: dict, suppliers: pd.DataFrame, margin_pct: float):
207
  rows = []
208
  for _, s in candidates.iterrows():
209
  factor = float(s["price_competitiveness_factor"])
210
- supplier_cost = market_mid * factor * random.uniform(0.92, 1.06)
211
 
212
  region = str(s["region"])
213
  if region in ["Johor", "KL", "Batam"]:
214
- supplier_cost *= 1.05
215
 
216
- supplier_cost = round(supplier_cost, 2)
217
- sell_price = round(supplier_cost / (1 - margin_pct / 100.0), 2)
218
 
219
  reliability = float(s["reliability_score"])
220
  lead = int(s["lead_time_days"])
@@ -227,7 +236,7 @@ def compute_offers(req: dict, suppliers: pd.DataFrame, margin_pct: float):
227
  "reliability_score": reliability,
228
  "lead_time_days": lead,
229
  "moq": int(s["moq"]),
230
- "est_supplier_cost_sgd": supplier_cost,
231
  "recommended_sell_price_sgd": sell_price,
232
  "score": round(score, 4),
233
  "contact_email": s["contact_email"],
@@ -237,27 +246,72 @@ def compute_offers(req: dict, suppliers: pd.DataFrame, margin_pct: float):
237
  return offers, (market_lo, market_hi)
238
 
239
 
240
- def build_quote_text(req, offers_df, pricing_mode, margin_used, company_name, customer_name, valid_days):
 
 
 
 
 
 
241
  if offers_df.empty:
242
- return (
243
- "No matching suppliers found.\n\n"
244
- "New Product Mode:\n"
245
- "1) Search market range online\n"
246
- "2) Identify supplier categories\n"
247
- "3) Send RFQs to shortlisted suppliers\n"
248
- "4) Update internal catalog once confirmed\n"
249
- )
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  qty = int(req.get("quantity") or 10)
252
  category = req.get("category") or "Lighting Product"
253
  brand = req.get("brand") or "Brand-agnostic"
254
  wattage = f"{req.get('wattage')}W" if req.get("wattage") else ""
255
- unit_price = float(offers_df.iloc[0]["recommended_sell_price_sgd"])
256
- total = round(unit_price * qty, 2)
257
  valid_until = (datetime.today() + timedelta(days=int(valid_days))).strftime("%Y-%m-%d")
258
 
259
- best = offers_df.iloc[0]
260
-
261
  return f"""Subject: Quotation - {brand} {wattage} {category} (Qty: {qty})
262
 
263
  Hi {customer_name},
@@ -266,10 +320,10 @@ Thanks for your inquiry. Please find our quotation below:
266
 
267
  Item: {brand} {wattage} {category}
268
  Quantity: {qty}
269
- Unit Price: SGD {unit_price:.2f}
270
  Total: SGD {total:.2f}
271
 
272
- Estimated Lead Time: {int(best["lead_time_days"])} days
273
  Validity: Until {valid_until}
274
  Pricing Mode: {pricing_mode} (Margin applied: {margin_used:.0f}%)
275
 
@@ -279,41 +333,111 @@ Sales Team
279
  """
280
 
281
 
 
 
 
282
  def run_agent(inquiry_text, base_margin, pricing_mode, top_n, company_name, customer_name, valid_days):
283
  req = parse_inquiry(inquiry_text)
284
  margin_used = pick_margin(pricing_mode, float(base_margin))
285
 
286
  offers_df, market_rng = compute_offers(req, SUPPLIERS_DF, margin_used)
287
 
288
- market_text = "Estimated market range: "
289
  if market_rng:
290
- market_text += f"SGD {market_rng[0]:.2f} – {market_rng[1]:.2f} per unit"
291
  else:
292
  lo, hi = estimate_market_range(req.get("category"), req.get("wattage"))
293
- market_text += f"SGD {lo:.2f} – {hi:.2f} per unit"
294
 
295
  steps = []
296
  steps.append(f"Step 1 β€” Extracted requirement: {req}")
297
  steps.append(f"Step 2 β€” Market intelligence: {market_text}")
298
  steps.append(f"Step 3 β€” Pricing mode: {pricing_mode} | Margin applied: {margin_used:.0f}%")
 
299
  if offers_df.empty:
300
- steps.append("Step 4 β€” No internal matches found β†’ New Product Mode triggered.")
301
- else:
302
- steps.append(f"Step 4 β€” Shortlisted {min(len(offers_df), int(top_n))} suppliers; top recommendation: {offers_df.iloc[0]['supplier_name']}")
 
 
 
 
 
 
303
 
304
- offers_view = offers_df.head(int(top_n)) if not offers_df.empty else pd.DataFrame(
305
- columns=["supplier_id","supplier_name","region","reliability_score","lead_time_days","moq","est_supplier_cost_sgd","recommended_sell_price_sgd","score","contact_email"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  )
307
 
308
- quote_text = build_quote_text(req, offers_df, pricing_mode, margin_used, company_name, customer_name, valid_days)
309
 
310
- # Write quote text to a file for download
311
  quote_path = "/tmp/quote_draft.txt"
312
  with open(quote_path, "w", encoding="utf-8") as f:
313
  f.write(quote_text)
314
 
315
- # Return: parsed req (json-like), agent steps, market text, offers table, quote text, downloadable file, downloadable csv
316
- return req, "\n".join(steps), market_text, offers_view, quote_text, quote_path, DATA_PATH
317
 
318
 
319
  # ----------------------------
@@ -323,16 +447,16 @@ with gr.Blocks(title="Delight AI Agent (Prototype)") as demo:
323
  gr.Markdown(
324
  """
325
  # πŸ’‘ Delight AI Agent (Prototype)
326
- Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’ recommends pricing β†’ generates quotation draft.
327
  """
328
  )
329
 
330
- with gr.Row():
331
- inquiry = gr.Textbox(
332
- label="Customer Inquiry",
333
- lines=6,
334
- value="Hi, please quote best price for 50 pcs Philips 18W LED panel light. Delivery to Singapore in 2 weeks."
335
- )
336
 
337
  with gr.Row():
338
  base_margin = gr.Slider(5, 40, value=20, step=1, label="Base Margin (%)")
@@ -344,8 +468,10 @@ Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’
344
  customer_name = gr.Textbox(label="Customer name", value="Customer")
345
  valid_days = gr.Slider(1, 30, value=7, step=1, label="Quote validity (days)")
346
 
 
347
  run_btn = gr.Button("πŸš€ Run Agent")
348
 
 
349
  with gr.Row():
350
  parsed_req = gr.JSON(label="Extracted Requirement")
351
  agent_steps = gr.Textbox(label="Agent Steps", lines=10)
@@ -353,11 +479,19 @@ Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’
353
  market_info = gr.Textbox(label="Market Intelligence", lines=2)
354
 
355
  offers_table = gr.Dataframe(
356
- label="Recommended Supplier Options (Top N)",
357
  interactive=False,
358
  wrap=True
359
  )
360
 
 
 
 
 
 
 
 
 
361
  quote_text = gr.Textbox(label="Generated Quote Draft", lines=14)
362
 
363
  with gr.Row():
@@ -369,10 +503,25 @@ Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’
369
  db_preview = gr.Dataframe(label="DB Preview (Top 10)", interactive=False)
370
  db_file = gr.File(label="Download Fresh DB (.csv)")
371
 
 
 
 
 
 
372
  run_btn.click(
373
  fn=run_agent,
374
  inputs=[inquiry, base_margin, pricing_mode, top_n, company_name, customer_name, valid_days],
375
- outputs=[parsed_req, agent_steps, market_info, offers_table, quote_text, quote_file, supplier_csv],
 
 
 
 
 
 
 
 
 
 
376
  )
377
 
378
  regen_btn.click(
@@ -382,5 +531,4 @@ Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’
382
  )
383
 
384
  if __name__ == "__main__":
385
- # Hugging Face uses 7860 by default; this makes it explicit and stable.
386
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
+ # app.py (Gradio) β€” Delight AI Agent Prototype with RFQ Simulation
2
+ # βœ… Stable on Hugging Face Gradio Spaces
3
+ # βœ… Dummy supplier DB (~50) stored in /tmp
4
+ # βœ… Inquiry β†’ extract β†’ shortlist β†’ RFQ simulation β†’ pick best β†’ quote draft β†’ download
5
+
6
  import os
7
  import re
8
  import random
9
+ import uuid
10
  from datetime import datetime, timedelta
11
 
12
  import pandas as pd
 
91
 
92
 
93
  # ----------------------------
94
+ # Parsing
95
  # ----------------------------
96
  def normalize_text(t: str) -> str:
97
  return re.sub(r"\s+", " ", (t or "").strip().lower())
 
188
  return base_margin
189
 
190
 
191
+ # ----------------------------
192
+ # Offers + RFQ Simulation
193
+ # ----------------------------
194
  def compute_offers(req: dict, suppliers: pd.DataFrame, margin_pct: float):
195
  category = req.get("category")
196
  brand = req.get("brand")
 
216
  rows = []
217
  for _, s in candidates.iterrows():
218
  factor = float(s["price_competitiveness_factor"])
219
+ supplier_cost_est = market_mid * factor * random.uniform(0.92, 1.06)
220
 
221
  region = str(s["region"])
222
  if region in ["Johor", "KL", "Batam"]:
223
+ supplier_cost_est *= 1.05
224
 
225
+ supplier_cost_est = round(supplier_cost_est, 2)
226
+ sell_price = round(supplier_cost_est / (1 - margin_pct / 100.0), 2)
227
 
228
  reliability = float(s["reliability_score"])
229
  lead = int(s["lead_time_days"])
 
236
  "reliability_score": reliability,
237
  "lead_time_days": lead,
238
  "moq": int(s["moq"]),
239
+ "est_supplier_cost_sgd": supplier_cost_est,
240
  "recommended_sell_price_sgd": sell_price,
241
  "score": round(score, 4),
242
  "contact_email": s["contact_email"],
 
246
  return offers, (market_lo, market_hi)
247
 
248
 
249
+ def simulate_rfq(offers_df: pd.DataFrame, qty: int, n: int = 5) -> pd.DataFrame:
250
+ """
251
+ Simulate supplier quote replies for top N suppliers.
252
+ - Price = est cost with small variance
253
+ - Lead time = existing lead +/- small variance
254
+ - Response time depends on reliability
255
+ """
256
  if offers_df.empty:
257
+ return pd.DataFrame()
 
 
 
 
 
 
 
258
 
259
+ top = offers_df.head(n).copy()
260
+
261
+ rfq_rows = []
262
+ for _, row in top.iterrows():
263
+ reliability = float(row["reliability_score"])
264
+ base_cost = float(row["est_supplier_cost_sgd"])
265
+ base_lead = int(row["lead_time_days"])
266
+
267
+ # More reliable suppliers give tighter pricing variance and faster response
268
+ price_variance = (1.5 - reliability) * 0.08 # 0.04–0.07ish
269
+ quoted_unit = base_cost * random.uniform(1 - price_variance, 1 + price_variance)
270
+ quoted_unit = round(quoted_unit, 2)
271
+
272
+ # Volume discount for higher qty (simple demo)
273
+ if qty >= 100:
274
+ quoted_unit = round(quoted_unit * 0.97, 2)
275
+ elif qty >= 50:
276
+ quoted_unit = round(quoted_unit * 0.985, 2)
277
+
278
+ lead = base_lead + random.choice([-2, -1, 0, 1, 2])
279
+ lead = max(2, lead)
280
+
281
+ response_hours = int(max(1, (1.2 - reliability) * random.uniform(3, 12)))
282
+
283
+ rfq_rows.append({
284
+ "rfq_id": f"RFQ-{uuid.uuid4().hex[:6].upper()}",
285
+ "supplier_name": row["supplier_name"],
286
+ "supplier_id": row["supplier_id"],
287
+ "region": row["region"],
288
+ "reliability_score": reliability,
289
+ "quoted_unit_price_sgd": quoted_unit,
290
+ "quoted_total_sgd": round(quoted_unit * qty, 2),
291
+ "quoted_lead_days": lead,
292
+ "response_time_hours": response_hours,
293
+ })
294
+
295
+ rfq_df = pd.DataFrame(rfq_rows)
296
+
297
+ # Best = lowest unit price, tie-breaker: lead time, then reliability
298
+ rfq_df = rfq_df.sort_values(
299
+ ["quoted_unit_price_sgd", "quoted_lead_days", "reliability_score"],
300
+ ascending=[True, True, False]
301
+ ).reset_index(drop=True)
302
+
303
+ rfq_df.insert(0, "rank", range(1, len(rfq_df) + 1))
304
+ return rfq_df
305
+
306
+
307
+ def build_quote_text(req, unit_price_sgd, lead_days, pricing_mode, margin_used, company_name, customer_name, valid_days):
308
  qty = int(req.get("quantity") or 10)
309
  category = req.get("category") or "Lighting Product"
310
  brand = req.get("brand") or "Brand-agnostic"
311
  wattage = f"{req.get('wattage')}W" if req.get("wattage") else ""
312
+ total = round(unit_price_sgd * qty, 2)
 
313
  valid_until = (datetime.today() + timedelta(days=int(valid_days))).strftime("%Y-%m-%d")
314
 
 
 
315
  return f"""Subject: Quotation - {brand} {wattage} {category} (Qty: {qty})
316
 
317
  Hi {customer_name},
 
320
 
321
  Item: {brand} {wattage} {category}
322
  Quantity: {qty}
323
+ Unit Price: SGD {unit_price_sgd:.2f}
324
  Total: SGD {total:.2f}
325
 
326
+ Estimated Lead Time: {int(lead_days)} days
327
  Validity: Until {valid_until}
328
  Pricing Mode: {pricing_mode} (Margin applied: {margin_used:.0f}%)
329
 
 
333
  """
334
 
335
 
336
+ # ----------------------------
337
+ # Agent runners
338
+ # ----------------------------
339
  def run_agent(inquiry_text, base_margin, pricing_mode, top_n, company_name, customer_name, valid_days):
340
  req = parse_inquiry(inquiry_text)
341
  margin_used = pick_margin(pricing_mode, float(base_margin))
342
 
343
  offers_df, market_rng = compute_offers(req, SUPPLIERS_DF, margin_used)
344
 
 
345
  if market_rng:
346
+ market_text = f"Estimated market range: SGD {market_rng[0]:.2f} – {market_rng[1]:.2f} per unit"
347
  else:
348
  lo, hi = estimate_market_range(req.get("category"), req.get("wattage"))
349
+ market_text = f"Estimated market range: SGD {lo:.2f} – {hi:.2f} per unit"
350
 
351
  steps = []
352
  steps.append(f"Step 1 β€” Extracted requirement: {req}")
353
  steps.append(f"Step 2 β€” Market intelligence: {market_text}")
354
  steps.append(f"Step 3 β€” Pricing mode: {pricing_mode} | Margin applied: {margin_used:.0f}%")
355
+
356
  if offers_df.empty:
357
+ steps.append("Step 4 β€” No internal matches found β†’ New Product Mode.")
358
+ quote_text = (
359
+ "No matching suppliers found.\n\n"
360
+ "New Product Mode:\n"
361
+ "1) Research market range online\n"
362
+ "2) Identify supplier categories\n"
363
+ "3) Send RFQs to shortlisted suppliers\n"
364
+ "4) Update internal catalog once confirmed\n"
365
+ )
366
 
367
+ empty_offers = pd.DataFrame(columns=[
368
+ "supplier_id","supplier_name","region","reliability_score","lead_time_days","moq",
369
+ "est_supplier_cost_sgd","recommended_sell_price_sgd","score","contact_email"
370
+ ])
371
+ empty_rfq = pd.DataFrame(columns=[
372
+ "rank","rfq_id","supplier_name","supplier_id","region","reliability_score",
373
+ "quoted_unit_price_sgd","quoted_total_sgd","quoted_lead_days","response_time_hours"
374
+ ])
375
+
376
+ quote_path = "/tmp/quote_draft.txt"
377
+ with open(quote_path, "w", encoding="utf-8") as f:
378
+ f.write(quote_text)
379
+
380
+ return req, "\n".join(steps), market_text, empty_offers, empty_rfq, "", quote_text, quote_path, DATA_PATH
381
+
382
+ steps.append(f"Step 4 β€” Shortlisted top {int(top_n)} suppliers from internal DB.")
383
+
384
+ offers_view = offers_df.head(int(top_n)).copy()
385
+
386
+ # Default quote based on internal estimate from best supplier
387
+ best = offers_view.iloc[0]
388
+ est_cost = float(best["est_supplier_cost_sgd"])
389
+ unit_price = round(est_cost / (1 - margin_used / 100.0), 2)
390
+ lead_days = int(best["lead_time_days"])
391
+
392
+ quote_text = build_quote_text(req, unit_price, lead_days, pricing_mode, margin_used, company_name, customer_name, valid_days)
393
+
394
+ quote_path = "/tmp/quote_draft.txt"
395
+ with open(quote_path, "w", encoding="utf-8") as f:
396
+ f.write(quote_text)
397
+
398
+ # RFQ area empty until user clicks simulate
399
+ empty_rfq = pd.DataFrame(columns=[
400
+ "rank","rfq_id","supplier_name","supplier_id","region","reliability_score",
401
+ "quoted_unit_price_sgd","quoted_total_sgd","quoted_lead_days","response_time_hours"
402
+ ])
403
+
404
+ summary = (
405
+ f"Current recommendation (internal estimate): {best['supplier_name']} | "
406
+ f"Est Cost SGD {est_cost:.2f}/unit β†’ Sell SGD {unit_price:.2f}/unit | Lead {lead_days} days"
407
+ )
408
+
409
+ return req, "\n".join(steps), market_text, offers_view, empty_rfq, summary, quote_text, quote_path, DATA_PATH
410
+
411
+
412
+ def run_rfq_simulation(req, offers_df, base_margin, pricing_mode, company_name, customer_name, valid_days):
413
+ if offers_df is None or len(offers_df) == 0:
414
+ return pd.DataFrame(), "No offers available for RFQ simulation.", "", "", None
415
+
416
+ qty = int(req.get("quantity") or 10)
417
+ margin_used = pick_margin(pricing_mode, float(base_margin))
418
+
419
+ rfq_df = simulate_rfq(offers_df, qty=qty, n=min(5, len(offers_df)))
420
+
421
+ best = rfq_df.iloc[0]
422
+ rfq_best_unit = float(best["quoted_unit_price_sgd"])
423
+ rfq_best_lead = int(best["quoted_lead_days"])
424
+
425
+ # Update selling price based on RFQ best unit cost
426
+ sell_unit = round(rfq_best_unit / (1 - margin_used / 100.0), 2)
427
+
428
+ summary = (
429
+ f"RFQ best supplier: {best['supplier_name']} | "
430
+ f"Quoted Cost SGD {rfq_best_unit:.2f}/unit β†’ Sell SGD {sell_unit:.2f}/unit | "
431
+ f"Lead {rfq_best_lead} days | Response ~{int(best['response_time_hours'])}h"
432
  )
433
 
434
+ quote_text = build_quote_text(req, sell_unit, rfq_best_lead, pricing_mode, margin_used, company_name, customer_name, valid_days)
435
 
 
436
  quote_path = "/tmp/quote_draft.txt"
437
  with open(quote_path, "w", encoding="utf-8") as f:
438
  f.write(quote_text)
439
 
440
+ return rfq_df, "βœ… RFQ simulation completed. Best quote selected.", summary, quote_text, quote_path
 
441
 
442
 
443
  # ----------------------------
 
447
  gr.Markdown(
448
  """
449
  # πŸ’‘ Delight AI Agent (Prototype)
450
+ Paste a customer inquiry β†’ agent extracts requirement β†’ ranks suppliers β†’ (simulated RFQ) β†’ recommends pricing β†’ generates quotation draft.
451
  """
452
  )
453
 
454
+ # Inputs
455
+ inquiry = gr.Textbox(
456
+ label="Customer Inquiry",
457
+ lines=6,
458
+ value="Hi, please quote best price for 50 pcs Philips 18W LED panel light. Delivery to Singapore in 2 weeks."
459
+ )
460
 
461
  with gr.Row():
462
  base_margin = gr.Slider(5, 40, value=20, step=1, label="Base Margin (%)")
 
468
  customer_name = gr.Textbox(label="Customer name", value="Customer")
469
  valid_days = gr.Slider(1, 30, value=7, step=1, label="Quote validity (days)")
470
 
471
+ # Buttons
472
  run_btn = gr.Button("πŸš€ Run Agent")
473
 
474
+ # Outputs
475
  with gr.Row():
476
  parsed_req = gr.JSON(label="Extracted Requirement")
477
  agent_steps = gr.Textbox(label="Agent Steps", lines=10)
 
479
  market_info = gr.Textbox(label="Market Intelligence", lines=2)
480
 
481
  offers_table = gr.Dataframe(
482
+ label="Supplier Shortlist (Top N)",
483
  interactive=False,
484
  wrap=True
485
  )
486
 
487
+ gr.Markdown("## πŸ“‘ RFQ Simulation (Agentic Step)")
488
+ rfq_btn = gr.Button("πŸ“¨ Simulate RFQ to Top Suppliers")
489
+
490
+ rfq_status = gr.Textbox(label="RFQ Status", lines=2)
491
+ rfq_table = gr.Dataframe(label="RFQ Responses (Simulated)", interactive=False, wrap=True)
492
+
493
+ recommendation = gr.Textbox(label="Recommendation Summary", lines=2)
494
+
495
  quote_text = gr.Textbox(label="Generated Quote Draft", lines=14)
496
 
497
  with gr.Row():
 
503
  db_preview = gr.Dataframe(label="DB Preview (Top 10)", interactive=False)
504
  db_file = gr.File(label="Download Fresh DB (.csv)")
505
 
506
+ # State
507
+ state_req = gr.State({})
508
+ state_offers = gr.State(pd.DataFrame())
509
+
510
+ # Wire actions
511
  run_btn.click(
512
  fn=run_agent,
513
  inputs=[inquiry, base_margin, pricing_mode, top_n, company_name, customer_name, valid_days],
514
+ outputs=[parsed_req, agent_steps, market_info, offers_table, rfq_table, recommendation, quote_text, quote_file, supplier_csv],
515
+ ).then(
516
+ fn=lambda req, offers: (req, offers),
517
+ inputs=[parsed_req, offers_table],
518
+ outputs=[state_req, state_offers],
519
+ )
520
+
521
+ rfq_btn.click(
522
+ fn=run_rfq_simulation,
523
+ inputs=[state_req, state_offers, base_margin, pricing_mode, company_name, customer_name, valid_days],
524
+ outputs=[rfq_table, rfq_status, recommendation, quote_text, quote_file],
525
  )
526
 
527
  regen_btn.click(
 
531
  )
532
 
533
  if __name__ == "__main__":
 
534
  demo.launch(server_name="0.0.0.0", server_port=7860)