# shaelilwang/jacque architecture

> Architecture diagram of the GitHub repository shaelilwang/jacque, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-25). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/shaelilwang/jacque
- Repository: https://github.com/shaelilwang/jacque

## Overview

1. **Repo shape:** Jacque is a small, functionality-first product-finding system. It has a **Flask backend** plus a single **unstyled static HTML/JS frontend** (`static/index.html`). The backend is the integration hub; most other Python files are domain-specific modules or CLI entrypoints.

2. **Primary user flow (Channel3 mode):** The browser uploads an image to Flask at `/api/search`. The backend base64-encodes the image, calls **Channel3’s search API** with `CHANNEL3_API_KEY`, then returns a flattened list of product candidates shaped like `{title, brand, price, image, url}` for the frontend to render as buy links.

3. **Alternative search mode (scoped sites):** The repo also supports a second retrieval path, `/api/search_scoped`, which searches only a user-supplied allowlist of sites. That path uses the **Serper.dev Google Search API** with `site:` scoping and pagination. The site list is loaded from a local `sites.txt`-style file (`sites.example.txt` in the repo), keeping the search boundary explicit and controllable.

4. **Text assistance layer:** A separate backend endpoint, `/api/assist`, sends the uploaded image to **Anthropic Sonnet** (`claude-sonnet-4-6`) when the user clicks “Fill description.” Sonnet returns two structured descriptions: **items** and **style**. These are combined into an editable search query that then feeds either Channel3 search or scoped search.

5. **Search result normalization boundary:** Regardless of which retrieval backend is used, the system converges on a common “candidate product” shape before anything else happens. This normalization is important because the rest of the pipeline expects consistent fields even when source sites or APIs differ.

6. **Ranking pipeline:** The repo has a second major subsystem for turning retrieved candidates into a ranked list. It is intentionally split into two stages:  
   - **Extraction** in `extract.py`: a cheap LLM pass (Haiku) reads each candidate’s text and extracts garment type, silhouette/aesthetic tags, material/color, dimensions, and whether it is the target item. Missing facts remain `None`; they are not invented.  
   - **Scoring** in `ranker.py`: pure deterministic functions compute sub-scores for **target match, fit, taste, and budget**, then combine them into an overall score with confidence weighting.

7. **User/profile model boundary:** `profiles.py` defines the scoring inputs that are treated as user state: measurements, usual size, fit preference, budget, Kibbe body type, and a hardcoded taste spec. `ranker.py` consumes that profile rather than embedding user assumptions directly, which makes the scoring layer reusable and testable.

8. **Confidence-aware ranking behavior:** The ranking design explicitly treats uncertainty as first-class. Each extracted signal has confidence, and each sub-score’s effective weight is reduced by a confidence exponent. Items with no usable signal can fall out of the ranking entirely (`overall = None`), which is a deliberate “honest data” boundary.

9. **Entry points and operational modes:** `app.py` runs the Flask web app, `run_harness.py` is the CLI for the scoped-site search pipeline, and `run_ranker.py` is a demo runner for the ranking pipeline. This gives the repo three execution modes: interactive web search, CLI retrieval, and CLI ranking.

10. **Validation and support files:** `test_ranker.py` covers the deterministic scoring logic, which is the most stable and testable part of the system. `requirements.txt`, `.env.example`, and `sites.example.txt` define the external dependencies, secrets, and site allowlist needed to wire the architecture together.

11. **External dependencies that matter architecturally:** The system depends on **Anthropic** for description/extraction, **Channel3** or **Serper.dev** for retrieval, and environment variables for API keys. Those services define the main network boundaries in the architecture diagram: browser → Flask → external AI/search APIs → normalized candidates → ranking → frontend display.

Main entry points: [`ranker.py`](https://github.com/shaelilwang/jacque/blob/main/ranker.py) (Rank items), [`app.py`](https://github.com/shaelilwang/jacque/blob/main/app.py) (Flask app), [`static/index.html`](https://github.com/shaelilwang/jacque/blob/main/static/index.html) (Search UI), [`harness.py`](https://github.com/shaelilwang/jacque/blob/main/harness.py) (Scoped search)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_frontend["Frontend"]
  node_browser(("Browser<br/>client"))
  node_static_ui["Search UI<br/>static html/js<br/>[index.html]"]
end

subgraph group_api["Backend API"]
  node_app["Flask app<br/>web server<br/>[app.py]"]
  node_assist["Assist API<br/>llm endpoint"]
end

subgraph group_retrieval["Retrieval"]
  node_search_api["Search API<br/>image search"]
  node_scoped_search["Scoped search<br/>google search<br/>[harness.py]"]
  node_sites_file["Site allowlist<br/>site list<br/>[sites.example.txt]"]
  node_normalize["Normalize<br/>candidate mapper"]
end

subgraph group_ranking["Ranking"]
  node_extract["Extract facts<br/>llm pass<br/>[extract.py]"]
  node_profiles["Profile state<br/>user profile<br/>[profiles.py]"]
  node_ranker["Rank items<br/>deterministic scorer<br/>[ranker.py]"]
  node_cost["Cost model<br/>scoring helper<br/>[cost.py]"]
end

subgraph group_ops["Entry points"]
  node_run_harness["Search CLI<br/>cli entrypoint<br/>[run_harness.py]"]
  node_run_ranker["Ranker demo<br/>cli entrypoint<br/>[run_ranker.py]"]
  node_tests["Rank tests<br/>test suite<br/>[test_ranker.py]"]
end

node_browser -->|"uses"| node_static_ui
node_static_ui -->|"/api/search"| node_app
node_static_ui -->|"/api/assist"| node_app
node_app -->|"image to text"| node_assist
node_app -->|"Channel3"| node_search_api
node_app -->|"scoped retrieval"| node_scoped_search
node_scoped_search -->|"loads allowlist"| node_sites_file
node_search_api -->|"candidates"| node_normalize
node_scoped_search -->|"candidates"| node_normalize
node_normalize -->|"text fields"| node_extract
node_extract -->|"signals"| node_profiles
node_profiles -->|"inputs"| node_ranker
node_extract -->|"scored features"| node_ranker
node_ranker -->|"pricing logic"| node_cost
node_ranker -->|"ranked results"| node_static_ui
node_run_harness -->|"CLI"| node_scoped_search
node_run_ranker -->|"CLI"| node_ranker
node_tests -.->|"verifies"| node_ranker

click node_static_ui "https://github.com/shaelilwang/jacque/blob/main/static/index.html"
click node_app "https://github.com/shaelilwang/jacque/blob/main/app.py"
click node_scoped_search "https://github.com/shaelilwang/jacque/blob/main/harness.py"
click node_sites_file "https://github.com/shaelilwang/jacque/blob/main/sites.example.txt"
click node_extract "https://github.com/shaelilwang/jacque/blob/main/extract.py"
click node_profiles "https://github.com/shaelilwang/jacque/blob/main/profiles.py"
click node_ranker "https://github.com/shaelilwang/jacque/blob/main/ranker.py"
click node_cost "https://github.com/shaelilwang/jacque/blob/main/cost.py"
click node_run_harness "https://github.com/shaelilwang/jacque/blob/main/run_harness.py"
click node_run_ranker "https://github.com/shaelilwang/jacque/blob/main/run_ranker.py"
click node_tests "https://github.com/shaelilwang/jacque/blob/main/test_ranker.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_browser,node_static_ui toneBlue
class node_app,node_assist toneAmber
class node_search_api,node_scoped_search,node_sites_file,node_normalize toneMint
class node_extract,node_profiles,node_ranker,node_cost toneRose
class node_run_harness,node_run_ranker,node_tests toneIndigo
```

## Components

### Frontend

Browser-facing UI and static display layer

- **Browser**: client · Uploads images and renders candidate products
- [Search UI](https://github.com/shaelilwang/jacque/blob/main/static/index.html): static html/js · Single-page upload and results interface · `static/index.html`

### Backend API

Flask app and request orchestration

- [Flask app](https://github.com/shaelilwang/jacque/blob/main/app.py): web server · Main integration hub exposing search and assist endpoints · `app.py`
- **Assist API**: llm endpoint · Generates structured item and style descriptions from an image

### Retrieval

Channel3 and scoped-site search paths

- **Search API**: image search · Channel3-backed image search entry point
- [Scoped search](https://github.com/shaelilwang/jacque/blob/main/harness.py): google search · Searches allowlisted sites with site-scoped queries and pagination · `harness.py`
- [Site allowlist](https://github.com/shaelilwang/jacque/blob/main/sites.example.txt): site list · Explicit allowlist used by scoped search · `sites.example.txt`
- **Normalize**: candidate mapper · Flattens retrieval results into a common product candidate shape

### Ranking

Candidate extraction and scoring

- [Extract facts](https://github.com/shaelilwang/jacque/blob/main/extract.py): llm pass · Reads candidate text and extracts garment, fit, material, color, and target signals · `extract.py`
- [Profile state](https://github.com/shaelilwang/jacque/blob/main/profiles.py): user profile · Measurements, size, fit preference, budget, Kibbe type, and taste spec · `profiles.py`
- [Rank items](https://github.com/shaelilwang/jacque/blob/main/ranker.py): deterministic scorer · Computes target, fit, taste, and budget scores with confidence weighting · `ranker.py`
- [Cost model](https://github.com/shaelilwang/jacque/blob/main/cost.py): scoring helper · Support logic for ranking economics and weighting · `cost.py`

### Entry points

CLI runners and support files

- [Search CLI](https://github.com/shaelilwang/jacque/blob/main/run_harness.py): cli entrypoint · Command-line runner for scoped-site retrieval · `run_harness.py`
- [Ranker demo](https://github.com/shaelilwang/jacque/blob/main/run_ranker.py): cli entrypoint · Command-line runner for the ranking pipeline · `run_ranker.py`
- [Rank tests](https://github.com/shaelilwang/jacque/blob/main/test_ranker.py): test suite · Validates deterministic scoring behavior · `test_ranker.py`

## Connections

- Browser → Search UI: uses · Interactive upload and results display
- Search UI → Flask app: /api/search · Sends image search requests to Flask
- Search UI → Flask app: /api/assist · Requests generated item and style descriptions
- Flask app → Assist API: image to text · Calls Anthropic Sonnet for structured descriptions
- Flask app → Search API: Channel3 · Performs image-based product search
- Flask app → Scoped search: scoped retrieval · Searches only user-allowed sites
- Scoped search → Site allowlist: loads allowlist · Uses explicit site boundary for search scope
- Search API → Normalize: candidates · Returns raw products that are flattened into a common shape
- Scoped search → Normalize: candidates · Normalizes search results into product candidates
- Normalize → Extract facts: text fields · Feeds candidate text into feature extraction
- Extract facts → Profile state: signals · Produces structured signals evaluated against user state
- Profile state → Rank items: inputs · Supplies measurements, fit, budget, and taste context
- Extract facts → Rank items: scored features · Provides confidence-weighted item features
- Rank items → Cost model: pricing logic · Uses shared cost and budget-related helpers
- Rank items → Search UI: ranked results · Outputs sorted candidates for display
- Search CLI → Scoped search: CLI · Runs scoped-site retrieval from the command line
- Ranker demo → Rank items: CLI · Runs the ranking pipeline as a demo
- Rank tests → Rank items: verifies · Covers deterministic scoring logic
