"""Deterministic stand-in for the LLM (no GPU, no network). `mock_generate` returns exactly the JSON the real model is supposed to return: it picks the top rule-derived candidate and attaches sensible checks/safety. Used by the test suite and by any local dry-run that does not hit Modal. """ from __future__ import annotations import json import random CHECKS_DB = { "bearing": ["Inspect the bearing housing for play or heat.", "Spin the shaft by hand - roughness confirms wear.", "Apply bearing grease; if noise persists, replace."], "belt": ["Inspect the belt for cracks, glazing, or fraying.", "Check belt tension - should deflect ~10mm under thumb.", "Replace belt if glazing or cracking is visible."], "imbalance": ["Redistribute the load evenly inside the drum.", "Check that the appliance is level.", "Inspect shock absorbers or springs for damage."], "blade": ["Disconnect power and inspect each blade for cracks.", "Check for debris caught in the blade assembly.", "Replace blade if any damage is found."], "compressor": ["Listen for grinding or knocking from the compressor.", "Check refrigerant lines for frost or oil stains.", "Contact a certified HVAC technician."], "pump": ["Check the pump inlet filter for blockages.", "Listen for cavitation (gurgling sounds).", "Inspect the impeller for damage."], "magnetron": ["DO NOT open the casing - high voltage danger.", "If arcing sounds are heard, stop using immediately.", "Have a qualified technician inspect."], "chain": ["Check chain tension - 20-30mm vertical play.", "Look for stiff links or rust.", "Lubricate with bicycle chain oil."], "derailleur": ["Check derailleur hanger alignment.", "Inspect cable tension and adjust.", "Clean and lubricate pivot points."], "brush": ["Inspect motor brushes (less than 5mm = replace).", "Check the commutator for scoring.", "Clean commutator with contact cleaner."], "gear": ["Check gear lubrication - apply fresh grease.", "Listen for grinding under load.", "Replace gear set if teeth are worn."], "spray": ["Remove and inspect the spray arm for blockages.", "Check that it rotates freely by hand.", "Clean nozzles with a thin wire."], "turntable": ["Check if the motor coupling is intact.", "Ensure the roller ring is clean.", "If the motor hums but does not turn, it may be seized."], "roller": ["Inspect the drum rollers for flat spots.", "Check roller shaft for wear.", "Replace worn rollers in pairs."], "foreign": ["Check pockets for coins or buttons before loading.", "Open the drum and inspect between drum and tub.", "Remove any trapped foreign objects."], "evaporator": ["Check the evaporator fan for ice buildup.", "Inspect the fan blade for damage.", "Listen for grinding or squealing."], "condenser": ["Clean the condenser coils with a brush.", "Check the condenser fan motor bearings.", "Ensure adequate airflow around the unit."], "refrigerant": ["Check for oil stains on refrigerant lines.", "Listen for hissing near the evaporator.", "Call a certified HVAC technician for a leak test."], "cavitat": ["Check the drain pump filter for blockages.", "Listen for gurgling during the drain cycle.", "Inspect the impeller for debris."], "rod": ["STOP DRIVING IMMEDIATELY - rod knock is critical.", "Have the engine towed to a mechanic.", "Do not rev the engine."], "squeal": ["Check belt tension and condition.", "Inspect the belt tensioner for wear.", "Replace glazed or worn belts."], "whine": ["Check lubrication on the bearing.", "Listen for pitch changes under load.", "Replace the bearing if lubrication does not help."], "default": ["Inspect the most likely component.", "Listen again after each check.", "Consult a technician if it persists."], } SAFETY_DB = { "CRITICAL": "STOP USE IMMEDIATELY. Disconnect power.", "HIGH": "Disconnect power before inspecting.", "MEDIUM": "Use with caution. Schedule inspection soon.", "LOW": "Normal operation likely. Monitor and maintain.", } def mock_generate(prompt: str, candidates: list, features=None) -> str: """Return JSON for the top candidate — what the real model should produce.""" rng = random.Random(hash(prompt) % 2**31) if candidates: top = candidates[0] name, urgency, weight = top.name, top.urgency, top.weight else: name, urgency, weight = "Inconclusive", "LOW", 0.0 fault_lower = name.lower() checks = [] for keyword, pool in CHECKS_DB.items(): if keyword in fault_lower: checks = rng.sample(pool, min(3, len(pool))) break if not checks: checks = list(CHECKS_DB["default"]) confidence = max(10, min(95, int(weight * 100) + rng.randint(-3, 3))) return json.dumps({ "fault": name, "urgency": urgency, "checks": checks, "safety": SAFETY_DB.get(urgency, "None"), "confidence": confidence, })