shopstack / Docs /ARCHITECTURE.md
pranaysuyash's picture
Sync ShopStack 2026-06-15 round 2: primary-nav More, undo bar, freshness stamps, safe_render_html, home-flow TabContext
af69759 verified
|
Raw
History Blame Contribute Delete
28.1 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

ShopStack Architecture

Last updated: 2026-06-15 Version: 0.1.0 Purpose: Comprehensive architectural reference for the ShopStack shopping intelligence platform.


1. System Overview

ShopStack is a local-first, off-the-grid shopping intelligence platform that helps households know what they have, what to use soon, what to buy, what to skip, and where to buy from β€” without sending data to the cloud.

1.1 Design Philosophy

Principle Application
Local-first SQLite database (WAL mode), no cloud dependencies for core functionality
Off-the-grid All mock providers by default; real models via optional local backends (MLX, llama.cpp)
Decision-first Every workflow leads to a buy/skip/use-soon decision, not raw data
Traceable Every tool execution creates an auditable trace with PII redaction
Composable modules Six logical modules (ShopStock, ShopBasket, ShopCompare, etc.) share data through the same database

1.2 Architecture Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Gradio Blocks (app.py)                    β”‚
β”‚  13 tabs, workflow-header, custom CSS theme                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  shopstack.ui.screens/                        β”‚
β”‚  dashboard.py  shopping.py  market_lens.py  inventory.py     β”‚
β”‚  ask.py  traces.py  other.py  price.py  model_stack.py       β”‚
β”‚  portability.py  household.py  field_notes.py  _utils.py     β”‚
β”‚  (Each screen is a Gradio adapter: parse β†’ call β†’ render)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   shopstack.services/                         β”‚
β”‚  shopping.py  ─  shopping list normalization, classification, β”‚
β”‚                  Swiggy enrichment, list completion            β”‚
β”‚  market_lens.py ─  barcode scan, object detection, OCR, STT   β”‚
β”‚  dashboard.py  ─  today dashboard state assembly              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   shopstack.tools/registry.py                  β”‚
β”‚  11 tools: add/update/consume/move/find inventory items,      β”‚
β”‚  create shopping list, compare to inventory, record price,    β”‚
β”‚  use-soon, buy suggestions, export trace                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               shopstack.persistence/database.py                β”‚
β”‚  SQLite + WAL mode, 10 tables, 18 seeded locations, full CRUD β”‚
β”‚  Tables: inventory_lots, purchase_events, shopping_lists,     β”‚
β”‚  shopping_list_items, household_locations, movement_events,   β”‚
β”‚  price_observations, stores, traces, app_config               β”‚
β”‚  Views: price_history, agent_traces (compat aliases)          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 shopstack.providers/                          β”‚
β”‚  registry.py ──  factory wired from Settings                  β”‚
β”‚  interfaces.py ──  11 abstract ABCs                          β”‚
β”‚  mock_providers.py ──  full mock implementations             β”‚
β”‚  local_provider.py ──  MLX + llama.cpp                       β”‚
β”‚  openai_provider.py ──  cloud fallback                       β”‚
β”‚  whisper_provider.py ──  cloud STT                           β”‚
β”‚  local_whisper_provider.py ──  on-device STT                 β”‚
β”‚  runtime.py ──  RuntimeReport dataclass                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               shopstack.planner/                              β”‚
β”‚  engine.py ──  PlannerEngine orchestrates completeβ†’parseβ†’exec β”‚
β”‚  prompts.py ──  system prompt builder for tool-calling        β”‚
β”‚  parser.py ──  robust JSON + tool_call extraction             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                shopstack.market/                              β”‚
β”‚  schema.py ──  NormalizedMarketRecord, MarketSnapshot        β”‚
β”‚  normalization.py ──  size parser, unit prices, combo detect β”‚
β”‚  analytics.py ──  snapshot analytics, cheapest option finder β”‚
β”‚  basket.py ──  basket builder, canonical matching            β”‚
β”‚  metadata.py ──  produce shelf-life, waste-risk, storage     β”‚
β”‚  sources/swiggy.py ──  Swiggy loader, normalizer             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               Supporting modules                              β”‚
β”‚  decisions.py ──  7-class item decision engine (BUY/SKIP/...) β”‚
β”‚  portability.py ──  JSON/CSV export/import                   β”‚
β”‚  scanner.py ──  barcode decoding (pyzbar + zbarimg)          β”‚
β”‚  traces/export.py ──  PII redaction, JSONL export            β”‚
β”‚  model_registry.py ──  16 candidate model entries            β”‚
β”‚  module_registry.py ──  canonical module metadata            β”‚
β”‚  app_context.py ──  singleton wiring (db, tools, providers)  β”‚
β”‚  config.py ──  pydantic-settings, SHOPSTACK_ env prefix      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

2. Module Architecture

2.1 Module Map

Module Slug Tabs Dependencies Service Paths
ShopStock stock purchase, inventory, usesoon, map, portability β€” screens.inventory, portability
ShopBasket basket shopping ShopStock services.shopping, screens.shopping
ShopCompare compare prices Sources market.analytics, market.normalization
ShopLens lens market ShopStock services.market_lens, screens.market_lens, scanner
ShopMemory memory prices, notes β€” ui.views, screens.other
ShopAgent agent today, ask, trace ShopStock, ShopBasket, ShopMemory planner.*, decisions, traces.export
Sources sources (none) β€” market.sources.swiggy
Runtime runtime modelstack β€” screens.model_stack, model_registry, providers.runtime

2.2 Module Registry (shopstack/module_registry.py)

All module metadata is defined in a single canonical registry. Every UI surface that needs module info (names, tab labels, dependencies, service paths) imports from this registry β€” never hardcodes strings.

Key data structures:

  • ModuleMetadata dataclass (frozen) with slug, name, label, description, tab_ids, tab_labels, order, service_modules, depends_on, is_source
  • TAB_ORDER dict β€” explicit ordering for the Gradio tab bar
  • TAB_LABELS dict β€” canonical display names for every tab
  • Lookup helpers: get_by_slug(), get_by_tab_id(), tab_label(), tab_order(), navigation(), module_dependencies(), summary_table()

3. Data Layer

3.1 Database (SQLite, WAL mode)

Connection: check_same_thread=False (safe for Gradio multi-threaded access)

Tables:

Table Purpose Key Fields
inventory_lots Home inventory items lot_id, canonical_name, quantity, unit, storage_location_id, status, price_paid, expiry dates
purchase_events Purchase records event_id, canonical_name, quantity, total_price, store_name, source_type
shopping_lists Active shopping lists list_id, name, goal, is_active
shopping_list_items Items within lists item_id, list_id, canonical_name, priority, status, linked_lots
household_locations 18 seeded storage locations location_id, name, parent_location_id, location_type
movement_events Item location changes movement_id, lot_id, from/to location, source, confidence
price_observations Price history records price_id, canonical_name, quantity, unit, price, store_name, observation_date
stores Store metadata store_id, name, location, store_type
traces Workflow audit trail trace_id, input_type, user_goal, perception, decision, proposed_tool_calls
app_config Key-value config storage key, value

Views (backward-compat aliases):

  • price_history β†’ SELECT from price_observations
  • agent_traces β†’ SELECT from traces

Location Hierarchy (18 seeded):

Home
β”œβ”€β”€ Kitchen
β”‚   β”œβ”€β”€ Fridge
β”‚   β”‚   β”œβ”€β”€ Fridge Door
β”‚   β”‚   β”œβ”€β”€ Fridge Top Shelf
β”‚   β”‚   └── Fridge Vegetable Drawer
β”‚   β”œβ”€β”€ Freezer
β”‚   └── Pantry
β”‚       β”œβ”€β”€ Pantry Top Shelf
β”‚       β”œβ”€β”€ Pantry Middle Shelf
β”‚       └── Spice Box
β”œβ”€β”€ Bathroom
β”‚   β”œβ”€β”€ Bathroom Cabinet
β”‚   └── Under Bathroom Sink
β”œβ”€β”€ Bedroom
β”‚   └── Medicine Drawer
└── Balcony
    └── Balcony Cleaning Shelf

3.2 Pydantic Models (shopstack/schemas/models.py)

All domain models are Pydantic BaseModel classes in a single file:

  • InventoryLot, PurchaseEvent, DetectionEvent, OcrExtraction
  • ShoppingList, ShoppingListItem
  • VoiceCommand, ToolCall, Trace
  • Store, PriceObservation, HouseholdLocation, MovementEvent
  • TripWeatherContext, ItemCatalog

Key enums: Currency, ItemStatus, Priority, ListItemStatus, LocationType, SourceType, MovementSource, RuntimeMode

ID generation: uuid4().hex[:12] β€” 12-char hex IDs throughout.

3.3 Configuration (shopstack/config.py)

pydantic-settings with SHOPSTACK_ env prefix:

Variable Default Purpose
SHOPSTACK_DB_PATH data/shopstack.db Database file path
SHOPSTACK_APP_PORT 7860 Gradio server port
SHOPSTACK_OFF_THE_GRID true Use mock providers (no cloud)
SHOPSTACK_PLANNER_BACKEND mock Text generation/planning
SHOPSTACK_STT_BACKEND mock Speech-to-text
SHOPSTACK_TTS_BACKEND mock Text-to-speech
SHOPSTACK_VISION_BACKEND mock Vision/object detection
SHOPSTACK_OCR_BACKEND mock OCR
SHOPSTACK_SEGMENTATION_BACKEND mock Segmentation
SHOPSTACK_LOCAL_MODEL_REPO unsloth/Llama-3.2-3B-Instruct-GGUF Local model source
SHOPSTACK_LOCAL_WHISPER_SIZE tiny Local whisper model size
SHOPSTACK_OPENAI_API_KEY "" Cloud fallback key

4. Provider System

4.1 Provider Interfaces (shopstack/providers/interfaces.py)

11 abstract provider ABCs, each defining a capability:

Interface Key Methods Mock Behavior
STTProvider transcribe(audio_path) Returns predefined Hindi/Hinglish phrases
TTSProvider speak(text) Writes note about what would be spoken
VisionProvider analyze(image_path, prompt) Random samples from 26 common kitchen items
ObjectDetectionProvider detect(image_path) Plausible bounding boxes + confidences
GroundingProvider ground(image_path, text) Returns grounded item references
SegmentationProvider segment(image_path) Returns placeholder masks
OCRProvider extract_text(image_path) Returns mock extracted text
PlannerProvider plan(context) Returns structured multi-step plans
ToolCallParserProvider parse(response_text) Parses intent β†’ tool call candidates
EmbeddingsProvider embed(texts) Returns random 384-d vectors
ImageEditProvider edit(image_path, prompt) Returns a dummy edited image path

4.2 Provider Registry (shopstack/providers/registry.py)

The ProviderRegistry is a factory wired from Settings:

  • Reads SHOPSTACK_*_BACKEND env vars
  • Backend "mock" β†’ Mock*Provider (default)
  • Backend "local" β†’ LocalProvider (MLX or llama.cpp)
  • Backend "openai" β†’ OpenAIProvider (cloud)
  • Backend "local_whisper" β†’ LocalWhisperProvider
  • Falls back gracefully to mock if a real backend isn't available

4.3 Runtime Report (shopstack/providers/runtime.py)

RuntimeReport dataclass captures:

  • Provider name, backend, loaded status, capability count
  • Error state if provider failed to init
  • Used by the Model Stack UI tab

5. Tool System

5.1 Tool Registry (shopstack/tools/registry.py)

11 tools, each registered with name, description, args schema, and handler:

Tool Args Purpose
add_inventory_item canonical_name, display_name, quantity, unit, storage_location, category, purchase_date, price_paid Add new item to inventory
update_inventory_item lot_id, updates dict Update existing inventory item
consume_inventory_item lot_id, quantity Record consumption (partial or full)
move_inventory_item lot_id, to_location, from_location Move item between storage locations
find_item name (prefix search) Search inventory across names & locations
create_or_update_shopping_list goal, items list Create/update the active shopping list
compare_visible_item_to_inventory item_name Compare detected item against current stock
record_price_observation canonical_name, price, quantity, unit, store_name Record a price observation
get_use_soon_items days (default 3) Get items expiring or aging soon
get_next_buy_suggestions β€” Get suggestions for what to buy next
export_anonymized_trace trace_id Export an anonymized agent trace

6. Decision Engine (shopstack/decisions.py)

Every household item is classified into one of 7 categories:

Decision Color When
BUY Green Out of stock or running low, and needed
SKIP Gray Already have enough, recently bought, or high waste risk
USE_SOON Amber Existing stock is expiring or aging
OPTIONAL Blue Not urgent, good-to-have
COMPARE Purple Needs price/store/pack comparison
CONFIRM Red Uncertain data, needs human verification
WATCH Light Gray Not urgent, monitor

Classification logic (_classify()):

  1. If item is use_soon AND quantity > 0 β†’ USE_SOON
  2. If low_stock AND quantity <= 0 β†’ BUY
  3. If low_stock AND quantity > 0 β†’ BUY
  4. If on_list AND quantity > 0 β†’ SKIP (already have)
  5. If quantity > 0 AND well stocked β†’ SKIP
  6. Default β†’ WATCH

Data sources for classification:

  • Active inventory (DB)
  • Use-soon items (3-day threshold)
  • Active shopping list items
  • Recent purchase events (2-day window)
  • Market snapshot prices (Swiggy)
  • Produce metadata (shelf life, waste risk)

7. Market Intelligence System

7.1 Data Flow

Swiggy Instamart snapshot (CSV/JSON)
  β†’ market/sources/swiggy.py (load + normalize)
    β†’ market/schema.py (NormalizedMarketRecord, MarketSnapshot)
      β†’ domain/unit_price.py (size parser, unit prices, combo detection)
        β†’ market/analytics.py (price stats, cheapest finder)
          β†’ Services (shopping.py enrichment, dashboard.py)
            β†’ UI Screens (other.py swiggy views, shopping.py cards)

7.2 Normalization Pipeline (domain/unit_price.py)

Canonical business logic for size/unit normalization lives in shopstack/domain/unit_price.py. The original market/normalization.py is now a thin re-export shim (motto_v3 Β§7).

Raw Swiggy records are normalized through:

  1. Size parsing (parse_size) β€” extracts numeric quantity and unit from text like "500 g", "1 kg"
  2. Unit price calculation (compute_unit_prices) β€” per-kg for weight-based, per-L for volume, per-unit for piece items
  3. Canonical name mapping (resolve_canonical, CANONICAL_MAP) β€” e.g., "Fresh Tomatoes (Hybrid)" β†’ "tomato"
  4. Combo detection (canonicalize_name) β€” identifies multi-pack/assorted items

7.3 Produce Metadata (market/metadata.py)

Lookup table for ~80 common produce items with:

  • Shelf life in days
  • Waste risk (high/medium/low)
  • Storage recommendations
  • Use-first priority ranking

7.4 Basket Builder (market/basket.py)

Matches user item requests against available market records using:

  • Canonical name matching
  • Quantity/unit estimation
  • Cheapest option selection
  • Summary with total estimate

8. Planner System

8.1 Planner Engine (shopstack/planner/engine.py)

PlannerEngine orchestrates:

  1. Build prompt β€” system prompt + tool definitions + user question
  2. Get completion β€” calls ProviderRegistry planner backend
  3. Parse response β€” extracts JSON tool calls from LLM output
  4. Execute tools β€” runs through ToolRegistry, collects results
  5. Format response β€” human-readable output

8.2 Parser (shopstack/planner/parser.py)

Robust JSON extraction from LLM output:

  • Finds ````json` blocks
  • Falls back to regex for [{"tool":...,"args":{...}}] patterns
  • Returns empty list on failure (graceful degradation)

8.3 Prompts (shopstack/planner/prompts.py)

System prompt builder that generates tool-calling instructions including:

  • Current inventory state summary
  • Available tools with arg schemas
  • Usage examples
  • Output format constraints

9. UI Architecture

9.1 Gradio Tab Structure

13 tabs, wired in app.py using module_registry.tab_label() for canonical names:

Tab ID Display Label Screen Module Key Functions
today Today screens/dashboard.py today_dashboard() β€” 6-value return
ask Ask ShopStack screens/ask.py ask_shopstack(), voice add commands
shopping Shopping List screens/shopping.py Create/view/classify/complete lists
market Market Lens screens/market_lens.py Scan, compare, buy/skip decisions
purchase Add Purchase screens/inventory.py Form + batch purchase recording
inventory Find Item at Home screens/inventory.py Search, cards, consume
usesoon Use Soon screens/inventory.py Expiry alerts, consume batch
prices Price Memory Check screens/price.py Price history, intelligence
map Map screens/other.py Location view, move items
modelstack Model Stack screens/model_stack.py Budget, provider status
trace Traces screens/traces.py List, detail, export
portability Data screens/portability.py JSON/CSV export/import
notes Field Notes screens/field_notes.py Markdown editor

9.2 UI Components (shopstack/ui/components/cards.py)

HTML rendering helpers:

  • badge_html() β€” colored status badges
  • card() β€” styled card with header/body
  • empty_state() β€” empty state message
  • render_rows() β€” HTML table rows from dicts
  • render_decision_card() β€” decision with color-coded badge
  • render_grouped_cards() β€” grouped decision cards
  • render_metric() β€” metric display

9.3 UI Views (shopstack/ui/views.py)

Dataclass-returning view builders:

  • PriceMemoryView β€” price history data + chart info
  • FieldNotesView β€” field notes load/save
  • build_price_memory_view() β€” assembles price history + chart DataFrame

9.4 Error Boundary (shopstack/ui/screens/_utils.py)

@safe_render decorator catches exceptions in UI render functions and returns a graceful error HTML message instead of crashing the tab.


10. Service Layer

10.1 Shopping Service (shopstack/services/shopping.py)

Function Purpose
normalize_item_name(name) Normalize item names (lowercase, strip)
classify_shopping_items(items, tools) Classify items via LLM (buy/skip/use-soon)
enrich_items_with_swiggy(items) Add Swiggy price/availability data
complete_shopping_list_service(list_id, tools) Complete list β†’ add to inventory
mark_items_purchased_service(item_ids_json, tools) Mark items purchased

10.2 Market Lens Service (shopstack/services/market_lens.py)

Function Purpose
analyze_market_lens(image_path, audio_path, providers, tools) Full pipeline: detect β†’ compare β†’ decide
detect_barcodes(image_path) Decode barcodes from image
analyze_visible_items(image_path, providers, tools) Object detection + inventory comparison
enrich_market_prices(decisions) Add Swiggy price data to decisions
transcribe_audio(audio_path, providers) Speech-to-text

10.3 Dashboard Service (shopstack/services/dashboard.py)

Function Purpose
build_dashboard_state(db, tools) Assemble full dashboard state: inventory stats, use-soon, market basket, low-stock, recent purchases

11. Model Registry (shopstack/model_registry.py)

16 candidate model entries across 7 provider groups.

Parameter budget: ≀32B total active params (enforced by validate_active_model_budget()).

Active Models:

Model Group Params Runtime Backend Config
llama-3.2-3b-instruct (MLX) Planner 3.0B mlx PLANNER_BACKEND=local
llama-3.2-3b-gguf Planner 3.0B gguf (llama.cpp fallback)
local-whisper-tiny (MLX) STT 0.04B mlx STT_BACKEND=local_whisper

Total active: ≀6.04B params β€” well within 32B cap.


12. Trace System (shopstack/traces/export.py)

Every tool execution creates an agent trace stored in the database. Traces include:

  • Perception snapshots
  • Inventory context before/after
  • Decision rationale
  • Proposed tool calls
  • Human confirmation status

PII Redaction: Phone numbers (10+ digits), emails, Aadhar (12-digit), PAN (5+4+1), addresses. Generic name fields are preserved.

Export: JSONL format via export_traces().


13. Portability (shopstack/portability.py)

  • JSON export: Full inventory + price observations + purchase events + field notes
  • CSV export: Inventory items only
  • JSON import: Inventory + price observations + field notes (with dedup)
  • CSV import: Inventory items only (with dedup)
  • Schema version: 1.0

14. CI/CD

GitHub Actions workflow (.github/workflows/ci.yml):

  • Runs on push/PR to main
  • Sets up Python 3.13
  • Installs dependencies in dev mode
  • Runs full test suite
  • Runs benchmark suite

Pre-commit hook runs tools/sync-readme-stats to keep README test counts current.


15. Key Design Decisions

Decision Rationale
Single schemas file Models share enums; avoid circular imports
*Provider ABCs named Provider Clear naming convention prevents confusion
PurchaseEvent with per-item fields No separate join table for simple purchases
12-char hex IDs Short enough for prefix resolution, collision-resistant
WAL mode Concurrent read/write safe for Gradio
18 seeded locations Hierarchical, covers typical Indian household
No auto-purchase/payment scraping Design-level constraint; ShopStack advises, doesn't buy
PII redaction targeted Only phone/email/Aadhar/PAN/address; generic names preserved
Mock providers as default Full app works without any ML model loaded
_env_file=None in tests Prevents .env from affecting test results
shopstack.ui package All render logic consolidated; no orphan modules
service boundary extraction Product logic lives in services/; screens are Gradio adapters
module_registry canonical names No hardcoded tab labels anywhere in app.py

16. Future Architecture Targets

Area Planned Approach Status
Cloud inference fallback HF Inference API provider Built in providers/huggingface_provider.py (26 tests)
Modal cloud GPU Modal provider for heavy models Built in providers/modal_provider.py
Semantic search BGE-M3 + Nomic embeddings Both providers built in providers/embeddings_provider.py. Default is Nomic (config: embeddings_backend=nomic). Wired into services/search.py + services/find.py.
Receipt scanning OCR pipeline β†’ purchase creation Pipeline built: services/ocr_pipeline.py (3-stage) + services/receipt.py (full pipeline). OCR provider, Tesseract provider, and dedicated receipt screen exist.
Multi-retailer sources Blinkit, Zepto, DMart adapters All 4 built: Swiggy _swiggy_adapter.py, Blinkit _blinkit_adapter.py, Zepto _zepto_adapter.py, DMart _dmart_adapter.py. Cross-source comparison in _comparison.py.
Correction review UI Accept/reject corrections Built: dedicated ui/screens/corrections.py (264 lines), wired into Memory tab, per-row inline accept/reject buttons, DB correction_events table.
Multi-user auth user_id columns exist in DB Not started
Production deployment Docker + deployment config Not started