Team: Fenrir's Byte
Challenge: Tripletex — AI Accounting Agent
Competition: NM i AI 2026 (Norway's National AI Championship)
consider buying me a coffee: https://buymeacoffee.com/iglooo
A fault-tolerant AI accounting agent that accepts natural-language task prompts (in 7 languages) and executes them against the Tripletex v2 REST API. The agent handles 30+ distinct accounting task types — from creating employees and invoices to year-end closing and ledger analysis.
POST /solve
│
▼
┌─────────────────────┐
│ PDF/File Parser │ ← base64 decode, Gemini vision extract
└─────────────────────┘
│
▼
┌─────────────────────┐
│ LLM Router │ ← Gemini 2.5 Flash classifies task + extracts JSON data
│ (ROUTER_PROMPT) │
└─────────────────────┘
│ task_type + data
▼
┌─────────────────────┐ ┌──────────────────────────┐
│ Deterministic │─────▶│ 21 hardcoded handlers │
│ Handler dispatch │ │ (create_employee, invoice,│
│ │ │ travel_expense, etc.) │
└─────────────────────┘ └──────────────────────────┘
│ handler returns False
▼
┌─────────────────────┐
│ LLM Fallback │ ← ReAct loop, up to 35 iterations
│ (tripletex_api_call │ Gemini decides which API calls to make
│ tool) │
└─────────────────────┘
│
▼
{"status": "completed"}
LLM Router → Deterministic → Fallback layering: Most tasks hit a deterministic Python handler (fast, accurate, no token waste). If the handler fails or the task is too complex, the LLM fallback loop takes over with full API access.
fix_call() interceptor: All API calls pass through a normalisation function that corrects common LLM mistakes — POST → PUT on action endpoints, body → query params for /:payment, /:invoice, /:deliver, etc.
get_vat_type() with directional matching: Norwegian VAT has both sales (utgående) and purchase (inngående) types at the same percentage. I built keyword matching that distinguishes direction per call site, extended with multilingual keywords (befreit, exento, fradrag, libéré).
ensure_bank_account(): Tripletex refuses to generate invoices without a registered bank account. Every invoice-creating handler calls this first to set a valid MOD11 Norwegian account number on ledger account 1920, also setting isInvoiceAccount: true.
File context pipeline: PDFs are decoded and passed as _file_ctx (readable text) into handler data, enabling regex scraping for occupation codes, bank account numbers, and other fields that LLMs sometimes miss.
| Handler | Task |
|---|---|
| create_employee | Full onboarding: dept, employment details, standard time, occupation code |
| create_customer | Customer with address |
| create_supplier | Supplier registration |
| create_product | Product catalogue |
| create_department | Department creation |
| create_invoice | Multi-line invoice with per-line VAT |
| register_payment | Full payment using amountOutstanding |
| reverse_payment | Negative payment reversal |
| supplier_invoice | AP voucher with purchase VAT, PDF attachment |
| create_credit_note | Credit note via /:createCreditNote |
| create_travel_expense | Per-diem loop + costs, deliver |
| delete_travel_expense | Travel expense deletion |
| create_project | Project with manager |
| project_hours_invoice | Timesheet + invoice |
| project_milestone | Fixed-price project + partial invoice |
| project_lifecycle | Full lifecycle: customer→PM→project→timesheets→supplier cost→invoice |
| currency_payment | Agio/disagio exchange rate adjustments |
| post_voucher | Manual voucher with VAT auto-detection |
| create_dimension | Accounting dimensions + linked voucher |
| process_salary | Salary split (base + bonus) with employee tags |
| year_end_closing | Depreciation + prepaid reversal + tax provision |
| ledger_analysis | Cross-period expense comparison, top-3 project creation |
- Runtime: Python 3.11, FastAPI, uvicorn
- AI: Google Gemini 2.5 Flash via Vertex AI
- Infrastructure: Google Cloud Run (europe-west1)
- Auth: Tripletex Basic Auth (0:<session_token>) via competition proxy
pip install fastapi uvicorn requests google-genai
export GCP_PROJECT_ID=your-project
uvicorn main:app --host 0.0.0.0 --port 8000main.py # Full agent (~2400 lines) — router, handlers, fallback, API client
README.md # This file
REPORT.md # Detailed challenge report
LICENSE # MIT
MIT — see LICENSE