// agent

Private Quote & Material Estimator

by ozzo · Jul 12, 2026 Public

Choose how to run this agent

Local runs on your GPU. For usable speed it needs a WebGPU-capable browser — Chrome or Edge on a machine with a graphics card, or an Apple Silicon Mac. Without a supported GPU, pick OpenAI or Anthropic above instead.
Try it now

Requires an API key and an AgentOp account.

0 downloads
0 forks
0.0 rating

Description

Drop competing contractor quotes and compare them side by side — totals, inclusions, exclusions — then ask questions across all of them, fully private and in-browser.

Source Code

agent.py
import json
import re

# How many quote passages to retrieve per free-form question.
RAG_TOP_K = [[[RAG_TOP_K|6]]]
# How many retrieved passages per vendor go into the comparison prompt.
QUOTE_SNIPPETS = [[[QUOTE_SNIPPETS|3]]]
# How many priced lines per vendor go into the comparison prompt.
QUOTE_PRICED_LINES = [[[QUOTE_PRICED_LINES|8]]]

# One entry per ingested quote:
# {"source": name, "amount_lines": [...], "total_line": str or None}
_QUOTES = []

# Currency-ish amounts: symbol/code prefixed, or bare decimals like 1.234,56 / 249.00
_AMOUNT_RE = re.compile(
    r"(?:[$€£₺]|\b(?:USD|EUR|GBP|TRY|CHF)\b)\s?[0-9][0-9.,]*"
    r"|\b[0-9]{1,3}(?:[.,][0-9]{3})+(?:[.,][0-9]{2})?\b"
    r"|\b[0-9]+[.,][0-9]{2}\b"
)
_TOTAL_RE = re.compile(r"grand total|total|amount due|balance due|subtotal|sum", re.I)


def _extract_pdf_text(pdf_bytes):
    """(JS-callable) Extract plain text from a PDF's raw bytes via pypdf."""
    import io
    from pypdf import PdfReader

    reader = PdfReader(io.BytesIO(pdf_bytes))
    return "\n\n".join((page.extract_text() or "") for page in reader.pages)


def _scan_amounts(text):
    """Return (priced lines, best total-ish line) found in one quote's text."""
    amount_lines, total_line = [], None
    for raw in text.splitlines():
        line = " ".join(raw.split())
        if line and _AMOUNT_RE.search(line):
            amount_lines.append(line[:160])
            if _TOTAL_RE.search(line):
                total_line = line[:160]  # keep the last one — grand total is usually last
    return amount_lines, total_line


async def _ingest_quote(source, text):
    """(JS-callable) Index one vendor quote and scan its priced lines.

    Returns JSON {"source", "chunks", "priced_lines", "total_line"} for the
    vendor card UI (deterministic — never LLM output).
    """
    chunks = await agentop_rag.add_document(source, text)
    amount_lines, total_line = _scan_amounts(text)
    _QUOTES.append(
        {"source": source, "amount_lines": amount_lines, "total_line": total_line}
    )
    return json.dumps(
        {
            "source": source,
            "chunks": chunks,
            "priced_lines": len(amount_lines),
            "total_line": total_line or "",
        }
    )


async def _vendor_evidence(query):
    """Per-vendor evidence blocks: detected totals, priced lines, relevant passages."""
    hits = await agentop_rag.search(query, QUOTE_SNIPPETS * len(_QUOTES) + 4)
    by_vendor = {q["source"]: [] for q in _QUOTES}
    for h in hits:
        bucket = by_vendor.get(h["source"])
        if bucket is not None and len(bucket) < QUOTE_SNIPPETS:
            bucket.append(h["text"][:700])
    blocks = []
    for q in _QUOTES:
        lines = [f"VENDOR FILE: {q['source']}"]
        lines.append(f"Detected total line: {q['total_line'] or '(none detected)'}")
        if q["amount_lines"]:
            lines.append("Priced lines:")
            lines.extend(f"  - {a}" for a in q["amount_lines"][:QUOTE_PRICED_LINES])
        for i, snippet in enumerate(by_vendor[q["source"]]):
            lines.append(f"Relevant excerpt {i + 1}: {snippet}")
        blocks.append("\n".join(lines))
    return "\n\n".join(blocks)


async def _compare_quotes(focus):
    """(JS-callable) Build the normalized quote comparison via wllama."""
    if not _QUOTES:
        return "Drop at least one vendor quote first, then compare."
    focus = (focus or "").strip()
    query = focus or "scope of work, what is included and excluded, prices"
    evidence = await _vendor_evidence(query)
    focus_line = (
        f"Pay particular attention to: {focus}.\n" if focus else ""
    )
    prompt = (
        "You are comparing vendor quotes for the same job, using ONLY the "
        "EVIDENCE below (one block per vendor file).\n"
        f"{focus_line}"
        "Format exactly:\n"
        "## Quote by quote\n"
        "One '### <vendor file>' section per vendor: the bottom-line price if "
        "stated, what the quote includes, and what it excludes or leaves "
        "unclear.\n\n"
        "## Head-to-head\n"
        "Bullets: which stated total is lowest, scope differences, and items "
        "only some vendors cover. Quote prices exactly as written; if you "
        "compute a difference, show the numbers you used.\n\n"
        "## Questions to ask before deciding\n"
        "Bullets: what to clarify with each vendor (missing prices, unclear "
        "scope).\n\n"
        "Never invent prices — write 'not stated' when something is missing.\n\n"
        f"EVIDENCE:\n{evidence}"
    )
    # TEMPLATE_SYSTEM_PROMPT only exists as a JS global; the query bridge falls
    # back to it when custom_prompt is empty (same idiom as the base template).
    return await process_user_query_wllama(
        prompt, globals().get("TEMPLATE_SYSTEM_PROMPT", "")
    )


async def process_user_query(query):
    """Free-form Q&A across all uploaded quotes (RAG, vendor-labelled).

    Overrides the default router so retrieval is deterministic (small local
    models are unreliable at deciding to call a search tool themselves).
    """
    if not _QUOTES:
        return (
            "I don't have any quotes yet — drop the vendor PDFs above and "
            "I'll compare them for you."
        )
    hits = await agentop_rag.search(query, RAG_TOP_K)
    excerpts = "\n\n".join(
        f"[{i + 1}] (from {h['source']}) {h['text']}" for i, h in enumerate(hits)
    ) or "(nothing relevant found)"
    grounded_prompt = (
        "Answer the QUESTION using ONLY the QUOTE EXCERPTS below. Name the "
        "vendor file for every fact or price you use, cite excerpts like [1], "
        "and quote prices exactly as written — if you compute a difference, "
        "show the numbers you used. If the answer is not in the excerpts, say "
        "the quotes do not state it.\n\n"
        f"QUOTE EXCERPTS:\n{excerpts}\n\nQUESTION: {query}"
    )
    return await process_user_query_wllama(
        grounded_prompt, globals().get("TEMPLATE_SYSTEM_PROMPT", "")
    )

More by ozzo

WhatsApp Sales Copilot

Turn a raw WhatsApp chat export into a mini CRM — typed quotes, bookings, payments and boarding pas…

Messy Itinerary Travel Planner

Drop your messy pile of booking PDFs and trip notes and get a clean day-by-day itinerary — plus war…

Semantic Search

Search your own notes or documents by meaning, not keywords — instant results with no LLM download.

Meeting Minutes

Turn a meeting recording into a summary, key decisions, and action items, then ask follow-up questi…

Voice Transcriber

Upload a voice note or audio file and get an instant transcript, then ask questions about it — all …

Document Q&A

Upload a PDF or text file and ask questions answered only from that document — fully in your browse…