Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/3099404236_ai-smart-accounting.json

1 line
8.8 KiB
JSON

{"repo": "3099404236/ai-smart-accounting", "files": {"ai_service.py": {"fa9af33ea8a31a3fe2347b3ebcbd51978ee188b8": {"t": 1785595162.5969257, "n": 8322, "h": "1c251c96f35e11c2", "c": "\"\"\"\nAI Service - Xiaomi MiMo API Integration\n\nUses Anthropic API compatible format with requests library\n\nFeatures:\n1. Automatic item type recognition\n2. Determine operating vs capital expenditure\n3. Estimate asset useful life\n\"\"\"\n\nimport json\nimport requests\nfrom datetime import date\n\nfrom config import XIAOMI_API_KEY, XIAOMI_MODEL, DEFAULT_LIFESPAN, EXPENSE_KEYWORDS\nfrom models import AIAnalysisResult\n\n# Anthropic API compatible endpoint\nAPI_URL = \"https://api.xiaomimimo.com/anthropic/v1/messages\"\n\n\ndef call_mimo_api(system_prompt: str, user_message: str) -> str:\n \"\"\"\n Call Xiaomi MiMo API (Anthropic format)\n\n Args:\n system_prompt: System prompt\n user_message: User message\n\n Returns:\n AI response text\n \"\"\"\n headers = {\n \"api-key\": XIAOMI_API_KEY,\n \"Content-Type\": \"application/json\"\n }\n\n payload = {\n \"model\": XIAOMI_MODEL,\n \"max_tokens\": 1024,\n \"system\": system_prompt,\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": user_message\n }\n ]\n }\n ],\n \"temperature\": 0.3,\n \"top_p\": 0.95,\n \"stream\": False\n }\n\n response = requests.post(API_URL, headers=headers, json=payload, timeout=30)\n response.raise_for_status()\n\n result = response.json()\n\n # Extract text from Anthropic format response\n content = result.get(\"content\", [])\n if content and len(content) > 0:\n return content[0].get(\"text\", \"\")\n\n return \"\"\n\n\ndef analyze_expense(description: str, amount: float) -> AIAnalysisResult:\n \"\"\"\n Analyze expense, determine type and estimate useful life\n\n Args:\n description: Expense description (e.g., \"bought a wok\")\n amount: Amount\n\n Returns:\n AIAnalysisResult: Analysis result\n \"\"\"\n today = date.today()\n date_str = today.strftime(\"%B %d, %Y\")\n weekday = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"][today.weekday()]\n\n # Build system prompt\n system_prompt = f\"\"\"You are MiMo, a professional personal finance analysis assistant developed by Xiaomi.\nToday's date: {weekday}, {date_str}. Your knowledge cutoff date is December 2024.\n\nYour task is to analyze the user's expense record and determine:\n1. Is this an \"operating expense\" or \"capital expenditure\"?\n - Operating expense: Consumed immediately, such as food, transportation, entertainment, phone bills\n - Capital expenditure: Items that can be used for a long time, such as appliances, furniture, electronics\n\n2. If it's a capital expenditure, estimate the reasonable useful life of the item\n\nPlease return in JSON format as follows:\n{{\n \"is_capital\": true or false,\n \"category\": \"item category\",\n \"item_name\": \"item name\",\n \"useful_life_years\": useful life in years (0 for operating expenses),\n \"reasoning\": \"brief reasoning\"\n}}\n\nReference useful life:\n- Phone: 3 years\n- Computer/Laptop: 4-5 years\n- TV: 8 years\n- Refrigerator/Washing machine/AC: 10-12 years\n- Cookware/Pans: 8-10 years\n- Furniture: 10 years\n- Clothes/Shoes: 2 years\n- Bicycle: 8 years\n- Car: 10 years\n\nNotes:\n- Low-value items (under $50) are usually treated as operating expenses\n- Subscription services (like annual memberships) are spread over the subscription period\n- Rent is spread over the lease term\n- Return only JSON, no other text\"\"\"\n\n user_prompt = f\"Please analyze this expense: {description}, amount: ${amount}\"\n\n try:\n response_text = call_mimo_api(system_prompt, user_prompt)\n\n # Try to parse JSON\n # Sometimes AI adds ```json and ```, need to handle\n if \"```json\" in response_text:\n response_text = response_text.split(\"```json\")[1].split(\"```\")[0].strip()\n elif \"```\" in response_text:\n response_text = response_text.split(\"```\")[1].split(\"```\")[0].strip()\n\n result = json.loads(response_text)\n\n return AIAnalysisResult(\n is_capital=result.get(\"is_capital\", False),\n category=result.get(\"category\", \"Other\"),\n item_name=result.get(\"item_name\", description),\n useful_life_years=result.get(\"useful_life_years\", 0),\n reasoning=result.get(\"reasoning\", \"\")\n )\n\n except json.JSONDecodeError as e:\n print(f\"AI response parsing failed: {e}\")\n print(f\"Raw response: {response_text}\")\n # Fallback to rule-based analysis\n return fallback_analysis(description, amount)\n\n except requests.exceptions.RequestException as e:\n print(f\"AI service network error: {e}\")\n # Fallback to rule-based analysis\n return fallback_analysis(description, amount)\n\n except Exception as e:\n print(f\"AI service call failed: {e}\")\n # Fallback to rule-based analysis\n return fallback_analysis(description, amount)\n\n\ndef fallback_analysis(description: str, amount: float) -> AIAnalysisResult:\n \"\"\"\n Fallback analysis (when AI service is unavailable)\n Uses simple rule-based judgment\n \"\"\"\n description_lower = description.lower()\n\n # Check if it's an operating expense\n for keyword in EXPENSE_KEYWORDS:\n if keyword in description_lower:\n return AIAnalysisResult(\n is_capital=False,\n category=\"Daily Expense\",\n item_name=description,\n useful_life_years=0,\n reasoning=f\"Contains keyword '{keyword}', classified as operating expense\"\n )\n\n # Check if it matches default lifespan\n for item, years in DEFAULT_LIFESPAN.items():\n if item in description_lower:\n return AIAnalysisResult(\n is_capital=True,\n category=item.title(),\n item_name=description,\n useful_life_years=years,\n reasoning=f\"Matches '{item}', using default lifespan of {years} years\"\n )\n\n # Judge by amount\n if amount < 50:\n return AIAnalysisResult(\n is_capital=False,\n category=\"Daily Expense\",\n item_name=description,\n useful_life_years=0,\n reasoning=\"Low amount, treated as operating expense\"\n )\n\n # Default as capital expenditure, use 3 years\n return AIAnalysisResult(\n is_capital=True,\n category=\"Other\",\n item_name=description,\n useful_life_years=3,\n reasoning=\"Default as capital expenditure, 3 years\"\n )\n\n\ndef estimate_purchase_impact(amount: float, useful_life_years: float) -> dict:\n \"\"\"\n Estimate the impact of a purchase on living costs\n\n Returns:\n {\n \"monthly_cost\": monthly cost,\n \"daily_cost\": daily cost,\n \"yearly_cost\": yearly cost\n }\n \"\"\"\n if useful_life_years <= 0:\n # Operating expense, recognized immediately\n return {\n \"monthly_cost\": amount,\n \"daily_cost\": amount / 30,\n \"yearly_cost\": amount * 12\n }\n\n months = useful_life_years * 12\n monthly_cost = amount / months\n daily_cost = amount / (useful_life_years * 365)\n yearly_cost = amount / useful_life_years\n\n return {\n \"monthly_cost\": round(monthly_cost, 2),\n \"daily_cost\": round(daily_cost, 2),\n \"yearly_cost\": round(yearly_cost, 2)\n }\n\n\nif __name__ == \"__main__\":\n # Test\n test_cases = [\n (\"bought a wok\", 300),\n (\"lunch today\", 35),\n (\"bought an iPhone 16\", 7999),\n (\"paid annual rent\", 36000),\n (\"taxi to office\", 25),\n (\"bought a washing machine\", 2500),\n ]\n\n for desc, amount in test_cases:\n print(f\"\\nAnalyzing: {desc} (${amount})\")\n result = analyze_expense(desc, amount)\n print(f\" Type: {'Capital' if result.is_capital else 'Operating'} expenditure\")\n print(f\" Category: {result.category}\")\n print(f\" Item: {result.item_name}\")\n if result.is_capital:\n print(f\" Useful life: {result.useful_life_years} years\")\n impact = estimate_purchase_impact(amount, result.useful_life_years)\n print(f\" Monthly cost: ${impact['monthly_cost']}\")\n print(f\" Reasoning: {result.reasoning}\")\n"}}}}