| import spaces |
| import gradio as gr |
| from transformers import AutoTokenizer, AutoModelForCausalLM |
| import torch |
| import json |
| import re |
|
|
| print("Loading Qwen2.5-1.5B-Instruct...") |
| model_name = "Qwen/Qwen2.5-1.5B-Instruct" |
| tokenizer = AutoTokenizer.from_pretrained(model_name) |
| model = AutoModelForCausalLM.from_pretrained(model_name, dtype=torch.float16, device_map="auto") |
| print("Ready!") |
|
|
| @spaces.GPU |
| def extract(email): |
| if not email.strip(): |
| return "Paste an email first" |
| |
| messages = [ |
| {"role": "system", "content": "You extract obligations from client emails. Respond with ONLY valid JSON, no other text."}, |
| {"role": "user", "content": f"""Analyze this client email and extract obligations. |
| |
| Email: |
| {email} |
| |
| Respond with ONLY this JSON format: |
| {{ |
| "client": "client name and company", |
| "project": "what they want built", |
| "promises": ["deliverable 1", "deliverable 2"], |
| "owes": ["what client must provide"], |
| "deadline": "when due", |
| "payment": "amount and terms", |
| "red_flags": ["scope creep or vague items"] |
| }}"""} |
| ] |
| |
| text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) |
| inputs = tokenizer(text, return_tensors="pt").to(model.device) |
| |
| outputs = model.generate( |
| **inputs, |
| max_new_tokens=500, |
| temperature=0.2, |
| do_sample=True, |
| pad_token_id=tokenizer.eos_token_id |
| ) |
| |
| response = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True) |
| |
| match = re.search(r'\{.*\}', response, re.DOTALL) |
| if match: |
| try: |
| data = json.loads(match.group()) |
| out = f"### π {data.get('client', '?')}\n\n" |
| out += f"**Project:** {data.get('project', '?')}\n\n" |
| out += f"**π
Deadline:** {data.get('deadline', '?')}\n\n" |
| out += f"**π° Payment:** {data.get('payment', '?')}\n\n" |
| out += "**β
Dev Promises:**\n" |
| for p in data.get('promises', []): |
| out += f"- {p}\n" |
| out += "\n**π Client Owes:**\n" |
| for o in data.get('owes', []): |
| out += f"- {o}\n" |
| out += "\n**π© Red Flags:**\n" |
| for r in data.get('red_flags', []): |
| out += f"- {r}\n" |
| return out |
| except Exception as e: |
| return f"Parse error: {e}\n\nRaw:\n{response[:500]}" |
| return f"No JSON found. Output:\n{response[:500]}" |
|
|
| with gr.Blocks() as app: |
| gr.Markdown("# πΌ Obligation Extractor\n**Build Small Hackathon β Qwen2.5 1.5B**\n\nPaste a client email to extract obligations, deadlines, payment terms, and red flags.") |
| email = gr.Textbox(label="Paste Client Email", lines=12, placeholder="Paste the full email here...") |
| btn = gr.Button("π Extract Obligations", variant="primary", size="lg") |
| output = gr.Markdown() |
| btn.click(extract, email, output) |
| gr.Examples([ |
| "Hi! Website redesign needed. Budget $5000 (50% upfront, 50% on completion). Due March 15. We provide content and feedback within 24h.", |
| "Hi, need a landing page + email signup. Budget around $1500. Pay on completion. Need it ASAP. Can you also do social media graphics?" |
| ], email) |
|
|
| app.launch() |