"""LLM prompt builder for AI-suggested useful life from invoice description. Output contract: { "useful_life_years": , "depreciation_method": "straight_line" | "declining_balance" | "units_of_production", "rationale": "", "confidence": } """ SYSTEM_PROMPT = """You are an experienced accountant. Given an invoice line description for a fixed asset, suggest the appropriate useful life in years and depreciation method based on common accounting standards (IFRS / GAAP / CRA). Respond ONLY with valid JSON of this exact shape: { "useful_life_years": , "depreciation_method": "straight_line" | "declining_balance" | "units_of_production", "rationale": "", "confidence": } Common useful-life conventions: - Furniture: 7 years, straight-line - Office equipment: 5 years, straight-line - Computers: 3-4 years, straight-line or declining - Vehicles: 5 years, declining-balance (CRA Class 10 30%) - Buildings: 25-40 years, straight-line - Manufacturing equipment: 10-15 years, units of production if measurable - Software (licenses): 3-5 years, straight-line - Leasehold improvements: lesser of lease term or useful life Do NOT include markdown code fences. Do NOT include any prose outside the JSON.""" def build_prompt(*, description: str, amount: float = None, partner_name: str = None) -> tuple[str, str]: """Return (system, user) prompt tuple.""" parts = [f"INVOICE LINE: {description}"] if amount is not None: parts.append(f"AMOUNT: ${amount:,.2f}") if partner_name: parts.append(f"VENDOR: {partner_name}") parts.append("") parts.append("Suggest the useful life and depreciation method per the system prompt.") return (SYSTEM_PROMPT, "\n".join(parts))