// 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.
Try it now

Requires an API key and an AgentOp account.

6 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.

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 js.callLLM(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

ATS Resume Optimizer Agent

The ATS Resume Optimizer Agent helps you tailor your resume to specific job postings and stand out …

Contract Plain-Language Explainer Agent

Contracts are written by lawyers, for lawyers — but you’re the one signing them. The Contract Plain…

Data Analysis Agent

The Data Analysis Agent is a powerful, browser-based AI agent built on AgentOp that lets you explor…