negotiation / app.py
ritik26esh's picture
Upload 2 files
5b4e64e verified
import os
import gradio as gr
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
MODEL_ID = os.getenv("MODEL_ID", "LiquidAI/LFM2.5-1.2B-Instruct")
HF_TOKEN = os.getenv("HF_TOKEN", None)
# Cache model/tokenizer so Space loads once
_tokenizer = None
_model = None
def load_llm():
global _tokenizer, _model
if _tokenizer is None or _model is None:
_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=HF_TOKEN)
# device_map="auto" uses GPU if available, else CPU
_model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
token=HF_TOKEN,
torch_dtype="auto",
device_map="auto",
)
_model.eval()
return _tokenizer, _model
def build_messages(
negotiation_type: str,
your_role: str,
counterpart_role: str,
relationship: str,
goal: str,
context: str,
constraints: str,
concessions: str,
deadline: str,
tone: str,
include_subject: bool,
signature_name: str,
email_thread: str,
):
# Negotiation-focused system prompt
system = f"""
You are an email drafting assistant specialized in negotiation emails.
Rules:
- Keep the tone: {tone}.
- Be respectful and professional. No threats, no insults, no pressure tactics.
- Use principled negotiation: be clear on interests, propose options, ask focused questions.
- Do not invent facts, dates, prices, legal terms, or policy. Use only what the user provides.
- Keep paragraphs short.
Output format:
{"- First line must be a subject line like: Subject: <...>\\n\\nThen the email body." if include_subject else "- Output ONLY the email body (no subject line)."}
End with a sign-off using the provided name if given.
""".strip()
user = f"""
Create a negotiation email draft.
Negotiation type: {negotiation_type}
Your role: {your_role}
Counterpart role: {counterpart_role}
Relationship/context: {relationship}
Goal / Ask:
{goal}
Background details (facts only):
{context}
Constraints / non-negotiables:
{constraints}
Possible concessions / flexibility:
{concessions}
Deadline / timing:
{deadline}
Existing email thread (if any):
{email_thread}
Signature name:
{signature_name}
""".strip()
return [
{"role": "system", "content": system},
{"role": "user", "content": user},
]
def generate_draft(
negotiation_type,
your_role,
counterpart_role,
relationship,
goal,
context,
constraints,
concessions,
deadline,
tone,
include_subject,
signature_name,
email_thread,
max_new_tokens,
temperature,
top_p,
top_k,
repetition_penalty,
):
tokenizer, model = load_llm()
messages = build_messages(
negotiation_type=negotiation_type,
your_role=your_role,
counterpart_role=counterpart_role,
relationship=relationship,
goal=goal,
context=context,
constraints=constraints,
concessions=concessions,
deadline=deadline,
tone=tone,
include_subject=include_subject,
signature_name=signature_name,
email_thread=email_thread,
)
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt")
inputs = {k: v.to(model.device) for k, v in inputs.items()}
with torch.no_grad():
out = model.generate(
**inputs,
max_new_tokens=int(max_new_tokens),
do_sample=True,
temperature=float(temperature),
top_p=float(top_p),
top_k=int(top_k),
repetition_penalty=float(repetition_penalty),
)
text = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True).strip()
# Ensure sign-off if user provided a name and model forgot it
if signature_name and signature_name.strip() and signature_name.strip() not in text[-150:]:
text = text.rstrip() + f"\n\nRegards,\n{signature_name.strip()}"
return text
with gr.Blocks(title="Negotiation Email Draft Tool (LFM2)") as demo:
gr.Markdown(
"# Negotiation Email Draft Tool (Powered by LFM2)\n"
"Fill the fields, then generate a negotiation-ready email draft.\n"
"Note: Output is a draft. Verify details before sending."
)
with gr.Row():
negotiation_type = gr.Dropdown(
["Salary/Offer", "Vendor Pricing", "Contract Terms", "Rent/Lease", "Timeline/Delivery", "Refund/Resolution", "Other"],
value="Vendor Pricing",
label="Negotiation Type",
)
tone = gr.Dropdown(
["Warm and collaborative", "Firm but polite", "Direct and concise", "Formal and cautious"],
value="Firm but polite",
label="Tone",
)
with gr.Row():
your_role = gr.Dropdown(
["Buyer/Client", "Seller/Vendor", "Candidate/Employee", "Employer", "Tenant", "Landlord", "Other"],
value="Buyer/Client",
label="Your Role",
)
counterpart_role = gr.Dropdown(
["Buyer/Client", "Seller/Vendor", "Recruiter/Employer", "Candidate/Employee", "Tenant", "Landlord", "Other"],
value="Seller/Vendor",
label="Counterpart Role",
)
relationship = gr.Textbox(label="Relationship Context (1–2 lines)", placeholder="Existing vendor relationship, new negotiation, prior discussions, etc.")
goal = gr.Textbox(label="Goal / Ask (be specific)", lines=3, placeholder="Example: Request 12% price reduction or extended payment terms (Net 45).")
context = gr.Textbox(label="Background Facts", lines=4, placeholder="Facts you want included (numbers, dates, scope, current offer, etc).")
constraints = gr.Textbox(label="Constraints / Non-Negotiables", lines=3, placeholder="Example: Budget cap, delivery deadline, must-have clause.")
concessions = gr.Textbox(label="Possible Concessions", lines=3, placeholder="Example: Longer contract term, faster payment, higher volume.")
deadline = gr.Textbox(label="Deadline / Timing", placeholder="Example: Need confirmation by Friday EOD.")
email_thread = gr.Textbox(label="Existing Email Thread (optional)", lines=6, placeholder="Paste prior messages here if this is a reply.")
signature_name = gr.Textbox(label="Signature Name (optional)", placeholder="Your name")
include_subject = gr.Checkbox(value=True, label="Include subject line")
with gr.Accordion("Generation settings (advanced)", open=False):
max_new_tokens = gr.Slider(120, 600, value=280, step=10, label="Max new tokens")
# Model card recommends low temperature/top_p for controlled output :contentReference[oaicite:3]{index=3}
temperature = gr.Slider(0.0, 1.0, value=0.1, step=0.05, label="Temperature")
top_p = gr.Slider(0.05, 1.0, value=0.1, step=0.05, label="Top-p")
top_k = gr.Slider(0, 200, value=50, step=5, label="Top-k")
repetition_penalty = gr.Slider(1.0, 1.3, value=1.05, step=0.01, label="Repetition penalty")
btn = gr.Button("Generate Draft")
output = gr.Textbox(label="Draft Email", lines=14)
btn.click(
fn=generate_draft,
inputs=[
negotiation_type, your_role, counterpart_role, relationship, goal, context,
constraints, concessions, deadline, tone, include_subject, signature_name,
email_thread, max_new_tokens, temperature, top_p, top_k, repetition_penalty
],
outputs=[output],
)
demo.queue().launch()