Update app.py
Browse filesMake prep list and replies deterministic
app.py
CHANGED
|
@@ -4,7 +4,6 @@ import gradio as gr
|
|
| 4 |
import torch
|
| 5 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 6 |
|
| 7 |
-
# MODEL_ID = "Qwen/Qwen2.5-0.5B-Instruct"
|
| 8 |
MODEL_ID = "Qwen/Qwen2.5-1.5B-Instruct"
|
| 9 |
|
| 10 |
ORDER_COLUMNS = [
|
|
@@ -23,32 +22,6 @@ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
|
|
| 23 |
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.float32)
|
| 24 |
model.eval()
|
| 25 |
|
| 26 |
-
# SYSTEM_PROMPT = """
|
| 27 |
-
# You extract customer orders from messy DMs for tiny sellers.
|
| 28 |
-
# Return only valid JSON with this exact shape:
|
| 29 |
-
# {
|
| 30 |
-
# "orders": [
|
| 31 |
-
# {
|
| 32 |
-
# "customer": "",
|
| 33 |
-
# "item": "",
|
| 34 |
-
# "quantity": "",
|
| 35 |
-
# "flavor": "",
|
| 36 |
-
# "pickup_time": "",
|
| 37 |
-
# "delivery_address": "",
|
| 38 |
-
# "payment_status": "",
|
| 39 |
-
# "notes": "",
|
| 40 |
-
# "missing_fields": []
|
| 41 |
-
# }
|
| 42 |
-
# ],
|
| 43 |
-
# "prep_list": [],
|
| 44 |
-
# "reply_drafts": [
|
| 45 |
-
# {"customer": "", "reply": ""}
|
| 46 |
-
# ]
|
| 47 |
-
# }
|
| 48 |
-
# Use empty strings for unknown values. Put missing details in missing_fields.
|
| 49 |
-
# """
|
| 50 |
-
|
| 51 |
-
|
| 52 |
|
| 53 |
SYSTEM_PROMPT = """
|
| 54 |
You are a careful order extraction engine for tiny sellers.
|
|
@@ -69,25 +42,26 @@ Extract customer orders from messy DMs. Return only valid JSON with this exact s
|
|
| 69 |
}
|
| 70 |
],
|
| 71 |
"prep_list": [],
|
| 72 |
-
"reply_drafts": [
|
| 73 |
-
{"customer": "", "reply": ""}
|
| 74 |
-
]
|
| 75 |
}
|
| 76 |
|
| 77 |
Critical rules:
|
| 78 |
-
-
|
| 79 |
-
-
|
| 80 |
-
- Do not
|
|
|
|
| 81 |
- Include every customer message that looks like an order or possible order.
|
| 82 |
-
- Use only facts explicitly present in
|
| 83 |
- If a value is unknown, use an empty string.
|
| 84 |
-
- Do not add order_id or total_cost
|
| 85 |
-
- For pickup orders, put
|
| 86 |
-
- If the customer is unsure, still include the order and
|
| 87 |
-
- missing_fields should only include
|
| 88 |
-
-
|
|
|
|
| 89 |
"""
|
| 90 |
|
|
|
|
| 91 |
EXAMPLE_INPUT = """Maya: Hi! Can I get 2 dozen cupcakes for Saturday morning? Half vanilla, half chocolate.
|
| 92 |
Sam: Need 1 birthday cake, chocolate, for pickup Friday 5pm. I can pay Venmo.
|
| 93 |
Lena: Do you still have lemon bars? I need some for tomorrow but not sure how many yet.
|
|
@@ -134,6 +108,79 @@ def format_replies(replies):
|
|
| 134 |
lines.append(f"**{customer}**\n\n{text}")
|
| 135 |
return "### Reply drafts\n\n" + "\n\n---\n\n".join(lines)
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
def analyze_messages(messages):
|
| 138 |
if not messages.strip():
|
| 139 |
return pd.DataFrame(columns=ORDER_COLUMNS), "Paste some DMs first.", "", ""
|
|
@@ -172,8 +219,14 @@ def analyze_messages(messages):
|
|
| 172 |
)
|
| 173 |
|
| 174 |
orders_df = normalize_orders(data)
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
raw = json.dumps(data, indent=2, ensure_ascii=False)
|
| 178 |
return orders_df, prep, replies, raw
|
| 179 |
|
|
|
|
| 4 |
import torch
|
| 5 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 6 |
|
|
|
|
| 7 |
MODEL_ID = "Qwen/Qwen2.5-1.5B-Instruct"
|
| 8 |
|
| 9 |
ORDER_COLUMNS = [
|
|
|
|
| 22 |
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.float32)
|
| 23 |
model.eval()
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
SYSTEM_PROMPT = """
|
| 27 |
You are a careful order extraction engine for tiny sellers.
|
|
|
|
| 42 |
}
|
| 43 |
],
|
| 44 |
"prep_list": [],
|
| 45 |
+
"reply_drafts": []
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
|
| 48 |
Critical rules:
|
| 49 |
+
- Treat each line as one separate customer message.
|
| 50 |
+
- The text before the first ":" is the customer name.
|
| 51 |
+
- Copy customer names exactly as written. Do not uppercase or lowercase them.
|
| 52 |
+
- Never copy details from one customer's message into another customer's order.
|
| 53 |
- Include every customer message that looks like an order or possible order.
|
| 54 |
+
- Use only facts explicitly present in that customer's own message.
|
| 55 |
- If a value is unknown, use an empty string.
|
| 56 |
+
- Do not add order_id or total_cost.
|
| 57 |
+
- For pickup orders, put pickup time in pickup_time. Put a pickup place or delivery address in delivery_address.
|
| 58 |
+
- If the customer is unsure, still include the order and describe the uncertainty in notes.
|
| 59 |
+
- missing_fields should only include fields the seller needs to ask for: quantity, flavor, pickup_time, delivery_address, payment_status.
|
| 60 |
+
- Always set prep_list to [].
|
| 61 |
+
- Always set reply_drafts to [].
|
| 62 |
"""
|
| 63 |
|
| 64 |
+
|
| 65 |
EXAMPLE_INPUT = """Maya: Hi! Can I get 2 dozen cupcakes for Saturday morning? Half vanilla, half chocolate.
|
| 66 |
Sam: Need 1 birthday cake, chocolate, for pickup Friday 5pm. I can pay Venmo.
|
| 67 |
Lena: Do you still have lemon bars? I need some for tomorrow but not sure how many yet.
|
|
|
|
| 108 |
lines.append(f"**{customer}**\n\n{text}")
|
| 109 |
return "### Reply drafts\n\n" + "\n\n---\n\n".join(lines)
|
| 110 |
|
| 111 |
+
def text_value(value):
|
| 112 |
+
if isinstance(value, list):
|
| 113 |
+
return ", ".join(str(v) for v in value if str(v).strip())
|
| 114 |
+
if value is None:
|
| 115 |
+
return ""
|
| 116 |
+
return str(value).strip()
|
| 117 |
+
|
| 118 |
+
def missing_list(order):
|
| 119 |
+
raw = order.get("missing_fields", [])
|
| 120 |
+
if isinstance(raw, str):
|
| 121 |
+
fields = [part.strip() for part in raw.split(",") if part.strip()]
|
| 122 |
+
else:
|
| 123 |
+
fields = [str(part).strip() for part in raw if str(part).strip()]
|
| 124 |
+
|
| 125 |
+
item = text_value(order.get("item"))
|
| 126 |
+
if item and not text_value(order.get("quantity")):
|
| 127 |
+
fields.append("quantity")
|
| 128 |
+
if item and not text_value(order.get("pickup_time")) and not text_value(order.get("delivery_address")):
|
| 129 |
+
fields.append("pickup_time")
|
| 130 |
+
if item and not text_value(order.get("payment_status")):
|
| 131 |
+
fields.append("payment_status")
|
| 132 |
+
|
| 133 |
+
return sorted(set(fields))
|
| 134 |
+
|
| 135 |
+
def build_prep_list(data):
|
| 136 |
+
items = []
|
| 137 |
+
for order in data.get("orders", []):
|
| 138 |
+
item = text_value(order.get("item"))
|
| 139 |
+
if not item:
|
| 140 |
+
continue
|
| 141 |
+
|
| 142 |
+
customer = text_value(order.get("customer")) or "customer"
|
| 143 |
+
quantity = text_value(order.get("quantity")) or "quantity to confirm"
|
| 144 |
+
flavor = text_value(order.get("flavor"))
|
| 145 |
+
|
| 146 |
+
line = f"{quantity} {item}"
|
| 147 |
+
if flavor:
|
| 148 |
+
line += f" ({flavor})"
|
| 149 |
+
line += f" - {customer}"
|
| 150 |
+
items.append(line)
|
| 151 |
+
|
| 152 |
+
return items
|
| 153 |
+
|
| 154 |
+
def build_reply_drafts(data):
|
| 155 |
+
replies = []
|
| 156 |
+
labels = {
|
| 157 |
+
"quantity": "quantity",
|
| 158 |
+
"flavor": "flavor",
|
| 159 |
+
"pickup_time": "pickup or delivery time",
|
| 160 |
+
"delivery_address": "pickup place or delivery address",
|
| 161 |
+
"payment_status": "payment status",
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
for order in data.get("orders", []):
|
| 165 |
+
customer = text_value(order.get("customer")) or "there"
|
| 166 |
+
item = text_value(order.get("item")) or "order"
|
| 167 |
+
quantity = text_value(order.get("quantity"))
|
| 168 |
+
flavor = text_value(order.get("flavor"))
|
| 169 |
+
missing = [labels.get(field, field) for field in missing_list(order)]
|
| 170 |
+
|
| 171 |
+
if missing:
|
| 172 |
+
needed = ", ".join(missing)
|
| 173 |
+
reply = f"Thanks, {customer}! I have your {item} order. Could you confirm the {needed}?"
|
| 174 |
+
else:
|
| 175 |
+
summary = f"{quantity} {item}".strip()
|
| 176 |
+
if flavor:
|
| 177 |
+
summary += f" ({flavor})"
|
| 178 |
+
reply = f"Thanks, {customer}! Confirming your order: {summary}."
|
| 179 |
+
|
| 180 |
+
replies.append({"customer": customer, "reply": reply})
|
| 181 |
+
|
| 182 |
+
return replies
|
| 183 |
+
|
| 184 |
def analyze_messages(messages):
|
| 185 |
if not messages.strip():
|
| 186 |
return pd.DataFrame(columns=ORDER_COLUMNS), "Paste some DMs first.", "", ""
|
|
|
|
| 219 |
)
|
| 220 |
|
| 221 |
orders_df = normalize_orders(data)
|
| 222 |
+
auto_prep = build_prep_list(data)
|
| 223 |
+
auto_replies = build_reply_drafts(data)
|
| 224 |
+
|
| 225 |
+
data["prep_list"] = auto_prep
|
| 226 |
+
data["reply_drafts"] = auto_replies
|
| 227 |
+
|
| 228 |
+
prep = format_list("Prep list", auto_prep)
|
| 229 |
+
replies = format_replies(auto_replies)
|
| 230 |
raw = json.dumps(data, indent=2, ensure_ascii=False)
|
| 231 |
return orders_df, prep, replies, raw
|
| 232 |
|