"""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()