// agent

Freelancer Tax Q&A Helper

by ozzo · Jul 03, 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. Check your machine
Try it now

Requires an API key and an AgentOp account.

71 downloads
0 forks
0.0 rating

Description

Freelancer Tax Q&A Helper is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.

What this agent can do

Freelancer Tax Q&A Helper is built from the Freelancer Tax Q&A Helper template. Runs fully on your own device: llama.cpp compiled to WebAssembly, GPU-accelerated through WebGPU, with no API key and no server. After the one-time model download it works offline. Can run on OpenAI models with your own API key, encrypted in your browser. Can run on Anthropic Claude models with your own API key, encrypted in your browser.

Source Code

agent.py
# Freelancer Tax Q&A Helper - Python side
# Public functions (no leading underscore) become tools; we expose only process_user_query.
# Do not import heavy packages; keep prompt compact for local models.

from typing import List, Tuple
import asyncio
from textwrap import shorten
import js

# Tunables (use triple-bracket templating with defaults)
MAX_CONTEXT_CHARS = int('[[[MAX_CHARS|6000]]]')
MAX_MEMORY_MESSAGES = 8  # keep it small for local models

# Conversation memory: list of (user, assistant)
HISTORY: List[Tuple[str, str]] = []

SYSTEM_PROMPT = (
    "You are a plain-language tax-question helper for freelancers and independent contractors. "
    "Answer questions about deductions, quarterly estimated taxes, and self-employment tax. "
    "Instructions:\n"
    "- Give clear, friendly explanations in simple sentences.\n"
    "- Start with a direct answer in 1-2 sentences.\n"
    "- Then include: 'Why:' with the general rule.\n"
    "- If helpful, add 'How to figure it:' with steps or a small example or quick math.\n"
    "- Ask 1-2 clarifying questions if key info is missing.\n"
    "- If the user states a jurisdiction, adapt to it. If not, assume U.S. federal rules and note when rules vary by state/country.\n"
    "- Do not invent precise statutory citations. You may reference high-level sources like 'IRS Publication 334/535' as examples.\n"
    "- If the topic is not tax, politely steer back to freelancer tax topics.\n"
    "- Always end with exactly this one-line reminder: This isn't professional tax advice.\n"
    "Formatting:\n"
    "- Use short paragraphs or bullet points.\n"
    "- Show simple calculations clearly.\n"
)


async def _llm_call(prompt: str, system_prompt: str) -> str:
    """Call the injected provider-neutral LLM from Python via JS.

    Args:
        prompt: The user/context prompt string.
        system_prompt: The system instruction string.
    """
    # window.callLLM(prompt, systemPrompt) is injected by the platform
    try:
        resp = await agentop_llm.generate(prompt, system_prompt)
    except Exception as e:
        return f"Sorry, I couldn't reach the language model service. Please try again. (Error: {e})"
    # resp should be a string
    try:
        return str(resp)
    except Exception:
        return "Sorry, I couldn't parse the model response. Please try again."


def _reset_state():
    """Reset in-memory conversation state (helper; not exposed as a tool)."""
    HISTORY.clear()


def _build_context(jurisdiction: str) -> str:
    """Build textual conversation context limited by character budget.

    Args:
        jurisdiction: The jurisdiction hint selected by the user.
    """
    parts: List[str] = []
    jur = jurisdiction.strip() or "US"
    parts.append(f"Jurisdiction: {jur}")
    parts.append("You are assisting a self-employed freelancer with taxes.")
    # Add recent history
    if HISTORY:
        parts.append("Conversation so far (most recent last):")
        for u, a in HISTORY[-MAX_MEMORY_MESSAGES:]:
            parts.append(f"User: {u}")
            parts.append(f"Assistant: {a}")
    context = "\n".join(parts)
    # Truncate if needed
    if len(context) > MAX_CONTEXT_CHARS:
        context = context[-MAX_CONTEXT_CHARS:]
    return context


async def process_user_query(query: str, jurisdiction: str = "US") -> str:
    """Handle a user query end-to-end: build context, call the LLM, update memory.

    Args:
        query: The user's tax question.
        jurisdiction: Jurisdiction hint (e.g., 'US', 'UK').
    """
    q = (query or "").strip()
    if not q:
        return "Please enter a freelancer tax question to begin. This isn't professional tax advice."

    context = _build_context(jurisdiction)
    # Compose final prompt: context + current turn
    prompt = (
        f"{context}\n\n"
        f"User: {q}\n"
        f"Assistant:"
    )

    answer = await _llm_call(prompt, SYSTEM_PROMPT)

    # Ensure the mandatory reminder is present (add if missing)
    reminder = "This isn't professional tax advice."
    if reminder not in answer:
        if answer.endswith("."):
            answer = f"{answer}\n\n{reminder}"
        else:
            answer = f"{answer}.\n\n{reminder}"

    # Update memory
    try:
        HISTORY.append((q, answer))
        # Trim memory if it grows too large
        if len(HISTORY) > MAX_MEMORY_MESSAGES * 2:
            del HISTORY[: len(HISTORY) - MAX_MEMORY_MESSAGES]
    except Exception:
        pass

    return answer

More by ozzo

Receipt & Invoice Extractor

Based on the Receipt & Invoice Extractor template.

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…