No updates detected yet. GitHub releases will appear here.
AI trade assistant — describe the job, get a one-trip material list with aisle numbers
翻訳待ち
Loading the full README. The saved excerpt is shown below.
Describe the job. Get a one-trip material list with aisle numbers.
Trades lose hours and margin on the second trip to the supply store — the fitting that didn't fit, the tool left back at the shop, the size that was wrong.
Ask Danny takes a job described the way someone actually says it:
and returns one list: materials with quantities and specs, the tools needed, a rough price, and the aisle number at Home Depot, Lowe's, or AutoZone.
The two problems this has to solveRead the full README ↗Describe the job. Get the steps, the parts, the cost, and where to find each part.
ask-danny-ai.com · Live, free beta
Trades lose hours and margin on the second trip to the supply store — the fitting that didn't fit, the tool left back at the shop, the size that was wrong.
Ask Danny takes a job described the way someone actually says it:
"My toilet keeps running"
and returns the steps to fix it plus one list: materials with quantities and specs, the tools needed, a rough price, and where each part is at Home Depot, Lowe's, or AutoZone.
Models invent products. A general model will happily produce a plausible part number for something that does not exist. So the server retrieves from a curated inventory (products.json, 249 products across 10 trade buckets) and injects the matches into its own prompt. Anything not in the inventory is included but marked low confidence.
Aisle numbers get the same treatment. A number is shown only when someone has checked it in that store; the model is told never to give one, and any that slips through is dropped. Every other part shows its department and section ("Plumbing — toilet repair"), and in four mapped Los Angeles Home Depots, which row that department is in and what it sits next to. No store has been checked yet, so today every part shows a department. The goal is 75–80% of shown aisle numbers correct at one store, and that ratio is the number worth improving before anything else.
Models sell the expensive part. Asked why an AC runs but does not cool, a model will reach for refrigerant. The usual answer is a $20 capacitor. The system prompt carries per-trade check-order guidance — capacitor before refrigerant, flapper before a new toilet, coils before a compressor, battery before a starter, jam wrench before a new disposal — because selling someone the costly part when a cheap one usually fixes it is the worst thing this tool can do.
There is also a hard stop above all of it: for a gas smell, a suspected leak, carbon monoxide, sparking or burning wiring, or standing water near live electricity, it returns no materials at all and says to leave and call the utility or the fire department. A short list of nothing plus the right instruction is the correct answer.
Several models answer independently, then one synthesises. This is not majority voting — a judge reads the drafts and produces a single list, weighing safety, technical correctness and compatibility.
Browser (index.html)
│
└── POST /api/council
│
▼
netlify/functions/ai-council.js
│
├── retrieval over products.json (server-side, not client-supplied)
├── system prompt built server-side (never accepted from the request)
├── persistent rate limit (Netlify Blobs — not enforcing yet, see Security)
│
├──► Gemini ─┐
├──► Groq ─┼──► drafts
│ │
└──► judge ───┘ synthesises one list
│
└── aisle numbers only from checked store data
If the council cannot be reached the client falls back to /api/ai, a single-model path that carries the same server-owned prompt and the same safety rules.
A circuit breaker opens after three consecutive failures from a provider and retries with a single trial request after a cooldown, so one sick provider does not slow every request.
| Voice input | Describe the job hands-free (Web Speech API) |
| Repair steps | 3 to 8 steps, safety first; none for hazards, only safe checks for pro work |
| Photo and video input | Show the problem instead of describing it; a video becomes four frames on the device |
| Store layouts | Where each department is in four mapped LA Home Depots, after asking for location once |
| Multi-store compare | Home Depot vs Lowe's, side by side |
| Tool checklist | What to bring, per trade |
| Offline mode | Last result cached for the truck |
| Quantity calculator | Coverage, runs and counts |
| Job history | Firestore when signed in, localStorage as a guest |
| Usage ring | How many free lists are left today |
Trades: Plumbing · HVAC · Carpentry · Auto · Appliance
Plans: free is 5 lists a day with up to 4 drafters, all on free-tier providers. Pro (40/hour, paid models) is built but not sold — verifyProToken() returns false until real accounts exist, and the tier is never taken from the request.
Architecture is easy to add and hard to justify. The thing that actually tells you whether this works is a set of real jobs and someone qualified marking the answers.
| Path | What it is |
|---|---|
eval/jobs.json | 58 jobs phrased as real people speak, 13 of them traps |
eval/run.js | Runs them against the live API, writes a CSV with columns for a tradesperson to grade |
eval/local-check.js | 200 checks against mocked providers — every deploy runs them and stops on a failure |
eval/run.js --compare claude | Runs every job through the free council and through Claude (Opus 5) with the identical prompt, and writes a blind A/B sheet for a grader plus a separate answer key. Answers whether the paid model is worth it before anyone pays for it. |
The traps are the interesting part: a gas smell must refuse rather than sell parts, an AC that won't cool must reach for the capacitor and not refrigerant, a humming disposal needs a jam wrench and not a new unit, a load-bearing wall needs an engineer, and a prompt injection must produce a normal list and leak nothing.
local-check.js runs both handlers in-process against scripted providers and has caught two ReferenceErrors that node --check could not see. Run it before every deploy.
This is the beginning of the README. Read the rest at the original. Read the full README ↗