Spaces:
Running
Running
Sync ShopStack 2026-06-15 round 2: primary-nav More, undo bar, freshness stamps, safe_render_html, home-flow TabContext
af69759 verified | # 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 | | |