PocketAccountant: custom ledger UI + deterministic agent (engine, ledger, retrieval, classifier)
c55ab5e verified | """Show the SAT classifier and auto-categorization (uses the deterministic fallback; | |
| the fine-tuned model plugs into the same interface). | |
| Run from the project root: python -m scripts.demo_finetune | |
| """ | |
| import sys | |
| try: | |
| sys.stdout.reconfigure(encoding="utf-8") | |
| except (AttributeError, ValueError): | |
| pass | |
| from src.agent import ToolContext, build_tools | |
| from src.finetune import RuleClassifier | |
| from src.ledger import Ledger | |
| def rule(t): | |
| print("\n" + "═" * 64 + f"\n{t}\n" + "═" * 64) | |
| def main(): | |
| rule("CLASSIFY free-text transactions → SAT account") | |
| c = RuleClassifier() | |
| samples = [ | |
| "Suscripción anual a Adobe Creative Cloud", | |
| "Carga de gasolina en Pemex rumbo a junta con cliente", | |
| "Compra de una MacBook Pro para la oficina", | |
| "Comida de negocios en Sanborns con un cliente", | |
| "Multa de tránsito por exceso de velocidad", | |
| "Recibo de CFE de la oficina", | |
| "Pago de despensa del supermercado", | |
| ] | |
| for s in samples: | |
| r = c.classify(s) | |
| flag = "deducible" if r.deducible else "NO deducible" | |
| ratio = f" ({r.deducible_ratio*100:.1f}%)" if r.deducible_ratio < 1 else "" | |
| print(f" {s[:46]:48} → {r.sat_code} {r.cuenta[:28]:30} [{flag}{ratio}, {r.kind}]") | |
| rule("AUTO-CATEGORIZE + BOOK in one step (agent tool)") | |
| lg = Ledger(":memory:") | |
| tools = build_tools(ToolContext(lg)) | |
| for date, desc, amt in [ | |
| ("2024-05-04", "Renovación de Figma", 300), | |
| ("2024-05-09", "Compra de laptop Dell para trabajo", 22000), | |
| ("2024-05-21", "Comida con cliente en VIPS", 800), | |
| ]: | |
| res = tools["record_expense_auto"].handler(date=date, description=desc, amount=amt) | |
| cl = res["classification"] | |
| print(f" {desc[:38]:40} → {cl['sat_code']} {cl['cuenta'][:24]:26} | {res['note']}") | |
| m = lg.month_totals(2024, 5) | |
| print(f"\n May deductible expenses (laptop excluded as asset): {m.deductible_expenses}") | |
| print(f" May IVA acreditable (incl. laptop's IVA): {m.iva_acreditable}") | |
| lg.close() | |
| print("\nThe fine-tuned MiniCPM replaces RuleClassifier behind the same interface " | |
| "and generalizes to brands/phrasings never seen in the keyword list.") | |
| if __name__ == "__main__": | |
| main() | |