// agent

Document Q&A

by ozzo · Jul 10, 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. This agent runs on-device only.
Try it now

Requires an API key and an AgentOp account.

38 downloads
0 forks
0.0 rating

Description

Upload a PDF or text file and ask questions answered only from that document — fully in your browser, nothing uploaded to a server.

Source Code

agent.py
import json

# How many document passages to retrieve per question (tunable at generation).
RAG_TOP_K = [[[RAG_TOP_K|4]]]


async def _ingest_document(source, text):
    """(JS-callable) Chunk + embed + index one document's text.

    Underscore-prefixed so it is never exposed to the LLM as a tool.
    Returns the number of chunks stored, as a string (for the UI).
    """
    count = await agentop_rag.add_document(source, text)
    return str(count)


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)


async def process_user_query(query):
    """RAG-first answering: retrieve the most relevant passages from the uploaded
    document(s), then answer grounded ONLY in them.

    Overrides the default router so retrieval is deterministic (small local
    models are unreliable at deciding to call a search tool themselves).
    """
    hits = await agentop_rag.search(query, RAG_TOP_K)
    if not hits:
        context = "(No document has been uploaded yet, or nothing relevant was found.)"
    else:
        context = "\n\n".join(
            f"[{i + 1}] (from {h['source']}) {h['text']}" for i, h in enumerate(hits)
        )
    grounded_prompt = (
        "Answer the QUESTION using ONLY the CONTEXT passages below. "
        "If the answer is not in the context, say you could not find it in the "
        "document. Cite passage numbers like [1] where relevant.\n\n"
        f"CONTEXT:\n{context}\n\nQUESTION: {query}"
    )
    # 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(
        grounded_prompt, globals().get("TEMPLATE_SYSTEM_PROMPT", "")
    )

More by ozzo

New Hire Handbook Q&A

Based on the New Hire Handbook Q&A template.

Contract Plain-Language Explainer

Upload a contract and get it explained in plain language — obligations, fees, deadlines, exit claus…

WhatsApp Sales Copilot

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

Private Quote & Material Estimator

Drop competing contractor quotes and compare them side by side — totals, inclusions, exclusions — t…

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.