Skip to content

Add pantry-pro: voice pantry inventory, expiry alerts, and meal ideas from stock - #368

Merged
Rizwan-095 merged 2 commits into
openhome-dev:devfrom
ileana-pr:add-pantry-pro
Aug 27, 2026
Merged

Add pantry-pro: voice pantry inventory, expiry alerts, and meal ideas from stock#368
Rizwan-095 merged 2 commits into
openhome-dev:devfrom
ileana-pr:add-pantry-pro

Conversation

@ileana-pr

Copy link
Copy Markdown
Contributor

🆕 New Community Ability

Ability Name: community/pantry-pro

What does it do?

Voice-guided pantry assistant for OpenHome. Tracks pantry and fridge inventory across sessions, suggests meals from what's already in stock (oldest food first), alerts before items expire, and builds a shopping list from used-up items and missing recipe ingredients.

Example conversation

User: "pantry pro"
OpenHome: "You have pasta, tomato sauce, and canned beans. Want recipe ideas?"
User: "yes"
OpenHome: "Using tomato sauce before it goes. I can do 1, Pasta and Beans. 2, Tomato and Herb Pasta. 3, Bean chilli. Pick a number, or say skip."
User: "1"
OpenHome: "Pasta and Beans. You're missing onion and garlic. Add those to the shopping list?"

APIs Required

None. Recipes use the free TheMealDB API (no key), with an LLM fallback if the request fails.

Checklist

  • main.py extends MatchingCapability with register_capability
  • README.md exists with description + setup instructions
  • No hardcoded API keys or secrets
  • Tested on OpenHome device/simulator
  • Files only in community/pantry-pro/

Summary

  • Adds PantryPro as a combined skill + background daemon
  • Persists inventory and shopping lists in pantrypro_inventory.json
  • Background daemon nudges when food expires today, tomorrow, or within 3 days

Test plan

  • Trigger with pantry pro on an empty pantry and add a few items with dates
  • Ask what's in the fridge / what's expiring
  • Ask what can I cook and pick a recipe; confirm missing items can go on the shopping list
  • Say we're out of milk and confirm restock offer
  • Confirm done returns to the Agent (resume_normal_flow)
  • Confirm expiry daemon speaks after the 90s grace (item expiring today/tomorrow)

Made with Cursor

… from stock

Co-authored-by: Cursor <cursoragent@cursor.com>
@ileana-pr
ileana-pr requested a review from a team as a code owner August 22, 2026 02:22
@github-actions

Copy link
Copy Markdown
Contributor

✅ Community PR Path Check — Passed

All changed files are inside the community/ folder. Looks good!

@github-actions github-actions Bot added the community-ability Community-contributed ability label Aug 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔀 Branch Merge Check

PR direction: add-pantry-prodev

Passedadd-pantry-prodev is a valid merge direction

@github-actions

Copy link
Copy Markdown
Contributor

✅ Ability Validation Passed

📋 Validating: community/pantry-pro
  ✅ All checks passed!

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Lint Results

__init__.py — Empty as expected

Files linted: community/pantry-pro/background.py community/pantry-pro/main.py

✅ Flake8 — Passed

✅ All checks passed!

@Rizwan-095 Rizwan-095 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ileana-pr , hope you're doing great. Nice work on this, the daemon contract is spot on and the delete-then-write storage is exactly right.

Two things:

In main.py, _load() treats "no file yet" and "couldn't read the file" the same, and since _save() deletes before writing, one failed read means the next "add milk" writes an empty pantry over the user's real inventory. Worth returning a success flag from _load() and having _save() refuse to write without it.

_find_item() matches both directions, so adding "almond milk" increments the existing "milk" instead of tracking it separately and "I used the almond milk" then deletes your milk. I'd make merging strict (exact, or singular/plural) and keep the fuzzy match only for lookups.

One nice improvement: the recipe prompt only uses inventory, but the platform already knows the user read_file("user_profile.md", in_ability_directory=False) gives you diet and household size. Read only, never write; more in docs/Agent-Memory-and-Context-Injection.md.

Thanks for this, nice work.

@Rizwan-095
Rizwan-095 merged commit 89ac152 into openhome-dev:dev Aug 27, 2026
10 checks passed
@ileana-pr

Copy link
Copy Markdown
Contributor Author

Thanks for the review — addressed all three notes in the latest commit:

  1. Load/save safety_load() now returns a success flag. A missing file is fine (empty pantry); a failed read sets load_ok=False, the session refuses to mutate, and _save() will not delete-then-write. Same guard on the background daemon.
  2. Item matching — merges (add) use exact or singular/plural only, so almond milk no longer bumps milk. Fuzzy substring match stays for lookups (used/remove/update), preferring the longest name.
  3. user_profile.md — recipe LLM fallback reads it for diet/household context (read-only, never written).

@ileana-pr

Copy link
Copy Markdown
Contributor Author

Follow-up PR with the three review fixes (load/save safety, strict item matching, user_profile.md for recipes): #371

uzair401 added a commit to ileana-pr/abilities that referenced this pull request Aug 31, 2026
does_match() hardcoded a HOTWORDS tuple that duplicated whatever gets configured
as this ability's dashboard trigger words -- a second source of truth that can
drift from the first with nothing to notice if it does. No other ability in the
repo overrides does_match() for this reason; invocation is left entirely to the
dashboard. Unlike a couple of other abilities reviewed this same pass, HOTWORDS
had no second job here (e.g. no in-conversation restart check reusing it), so
removing it needed nothing else adjusted.

Verified the three fixes this PR actually addresses are unaffected: strict
add-time matching still keeps "almond milk" distinct from "milk", fuzzy
lookup still resolves "eggs" to a stocked "egg", and the new item_date /
full_list logic added in this PR still runs correctly. File still imports
cleanly with no does_match anywhere in the class.

Separately, and NOT changed here: _search_meals/_lookup_meal call
session_tasks.get_async(), a method name that appears nowhere in the SDK
reference docs (only .create() and .sleep() are documented there) and that no
other ability in this repo uses -- everything else calls session_tasks.get()
or plain requests.get(). That code isn't touched by this PR's diff at all; it's
inherited from openhome-dev#368. Flagging it rather than guessing at a fix, since getting a
live SDK method name wrong by assumption risks trading a real bug for a
different one.
uzair401 added a commit that referenced this pull request Aug 31, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Uzair Ullah <uzairullahmail@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-ability Community-contributed ability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants