Gestalt is a Flask web app for AI-assisted PC builds: conversational intake, a CrewAI analysis + recommendation pipeline with compatibility validation, optional live pricing (Amazon / eBay), a Server-Sent Events log stream, and an “Explain Like I’m a Beginner” (ELI5) explainer backed by Google Gemini when configured.
Python 3.12–3.13. Dependencies are managed with uv (see pyproject.toml / uv.lock).
| Area | Behavior |
|---|---|
| Web UI | Single-page UI (templates/index.html, static/hexcore.css): prompt, crew trace terminal, parts table with buy links and savings rollup, ELI5 panel, responsive layout and motion (see recent UI PRs). |
| Intake | intake.py — before calling the crew, decides if the user prompt has enough detail (budget + use case, or a long brief). May return clarification questions; merges follow-up answers into the build prompt. |
| Crew | crew.py — analysis task → recommendation loop (up to 3 tries) with compatibility_checker.validate_build; builds agent_trace for the UI. Uses Gemini via CrewAI when GEMINI_API_KEY / GOOGLE_API_KEY is set; otherwise heuristics-only paths apply. |
| Catalog | parts.json + parts_catalog.py — bundled parts, prices, and compatibility inputs. |
| Pricing | price_comparison.py, amazon_api.py, ebay_api.py — optional live checks when API keys exist; otherwise catalog/list pricing. |
| ELI5 | eli5.py + POST /explain — plain-English explanation of a completed build (requires Gemini API key). |
gestalt/
├── app.py # Flask: /, /build, /build/stream, /explain
├── crew.py # Analysis + recommendation + validation trace
├── agents.py # CrewAI agents / LLM resolution
├── intake.py # Pre-build clarification
├── compatibility_checker.py
├── parts_catalog.py, parts.json
├── amazon_api.py, ebay_api.py, price_comparison.py
├── eli5.py
├── templates/index.html
├── static/hexcore.css
├── tests/ # pytest (unit + e2e pipeline)
├── .env.example
├── pyproject.toml
└── README.md
Versioned alongside the app: LICENSE, .gitignore, .python-version.
cd Gestalt
uv sync
cp .env.example .env
# Set GEMINI_API_KEY (Google AI Studio) for CrewAI + ELI5 — see .env.example
uv run gestalt-web
# http://127.0.0.1:5000CLI entrypoint: gestalt-web → app:main (Flask dev server). Defaults to http://127.0.0.1:5000; set PORT and HOST=0.0.0.0 if you need another bind (e.g. container or LAN).
uv run python crew.pyDeploy on Render (Web Service)
Use Python 3, branch main, empty Root Directory if this repo is the app root.
Build Command (pick one):
- pip (works without a valid
uv.lock):
pip install --upgrade pip && pip install . - uv (if your lockfile syncs cleanly):
uv sync --frozen --no-dev && uv cache prune --ci
Start Command (production WSGI — do not use flask run / gestalt-web on Render):
gunicorn app:app --bind 0.0.0.0:$PORT --workers 1 --threads 4 --worker-class gthread --timeout 300Render sets PORT automatically. Use --timeout 300 (or higher) so long SSE /build/stream responses are less likely to be cut off.
Environment variables in the Render dashboard (mirror .env.example):
| Variable | Notes |
|---|---|
GEMINI_API_KEY or GOOGLE_API_KEY |
Required for full CrewAI + ELI5 behavior. |
GESTALT_LLM_MODEL |
Optional LiteLLM id for Crew (e.g. gemini/gemini-2.5-flash). |
GESTALT_ELI5_MODEL |
Optional google.genai model id for /explain. |
RAINFOREST_API_KEY, SERPAPI_API_KEY |
Optional live pricing. |
FLASK_DEBUG |
Omit or 0 in production. |
Optional: commit render.yaml and use Render Blueprint to provision the service; edit plan / name as needed.
Copy .env.example → .env (never commit .env). Important keys:
| Variable | Required | Purpose | If missing |
|---|---|---|---|
GEMINI_API_KEY or GOOGLE_API_KEY |
Recommended | Enables CrewAI LLM path and ELI5 generation | Crew/intake fall back to heuristic paths; /explain returns 503 |
GESTALT_LLM_MODEL |
Optional | Override CrewAI/LiteLLM model id | Defaults to a Gemini model |
GESTALT_ELI5_MODEL |
Optional | Override google.genai model id for ELI5 |
Falls back to GESTALT_GEMINI_SMOKE_MODEL or default |
GESTALT_GEMINI_SMOKE_MODEL |
Optional | Model used by tests/test_gemini_smoke.py |
Defaults to gemini-2.5-flash |
RAINFOREST_API_KEY |
Optional | Amazon live price lookup | Pricing uses catalog/list price fallback |
SERPER_API_KEY |
Optional | Amazon fallback lookup (when Rainforest is not configured) | Amazon price may be unavailable/approximate |
SERPAPI_API_KEY |
Optional | Market price lookup via Google Shopping (legacy “eBay” slot) | Pricing uses catalog/list price fallback |
GESTALT_PC_BUILD_SERVICE_RATE |
Optional | Savings rollup rate for “build service fee avoided” | Defaults to 0.12 |
PORT |
Optional | Bind port (hosts like Render inject this) | Defaults to 5000 |
HOST |
Optional | Bind host | Defaults to 127.0.0.1 |
FLASK_DEBUG |
Optional | Local debug mode (1/true) |
Off by default |
GESTALT_VERSION |
Optional | Shown in /healthz + UI footer |
UI shows dev |
GIT_SHA |
Optional | Shown in /healthz + UI footer |
UI shows local |
- No LLM key (
GEMINI_API_KEY/GOOGLE_API_KEYmissing):\n - Intake and build pipeline use heuristic fallbacks.\n -/explain(ELI5) returns 503 with a clear message.\n- No pricing keys (RAINFOREST_API_KEY/SERPAPI_API_KEYmissing):\n - Live pricing is skipped.\n - The UI displays catalog/list pricing fromparts.jsonand still shows rollups.
| Method | Path | Purpose |
|---|---|---|
GET |
/ |
Main UI |
GET |
/healthz |
Health + optional metadata (version, commit) for deploy checks and judge scans. |
GET |
/version |
Runtime metadata (python version, uptime, optional version/commit). |
GET |
/metrics |
Minimal Prometheus-style counters and last build duration gauge (in-memory). |
POST |
/build |
JSON body: prompt, optional original_prompt + clarification_answers. Returns JSON build result or clarification payload. |
POST |
/build/stream |
Same body; SSE (text/event-stream) using proper SSE fields: `event: trace |
POST |
/explain |
JSON: build (object), optional analysis. Returns { "eli5": "..." } or error (503 if no API key for ELI5). |
Requests to /build, /build/stream, and /explain are validated with Pydantic at the HTTP boundary (see schemas.py). Unknown fields are ignored; invalid shapes return 400 with a details list.
Default (CI-friendly, no network):
uv run pytest tests/ -q- Unit / component tests — parsing, compatibility, intake, pricing math, crew helpers, mocked CrewAI, ELI5 helpers, etc.
- End-to-end pipeline —
tests/test_e2e_pipeline.py(markere2e): Flask test client againstGET /,POST /build,POST /build/stream,POST /explainwithapp.run_build_assistantand ELI5 mocked (no live LLM, no retailer APIs). Uses the real parts catalog to build a deterministic JSON crew payload.
Run only e2e tests:
uv run pytest tests/test_e2e_pipeline.py -v
# or
uv run pytest -m e2e -vOptional integration / smoke (needs keys or network):
uv run pytest tests/test_gemini_smoke.py -vMark integration is reserved for tests that need real API keys or network (see pyproject.toml markers).
If the server is running locally on port 5000, you can quickly verify SSE formatting end-to-end:
python3 scripts/sse_smoke.py
# or with a different base URL:
python3 scripts/sse_smoke.py http://127.0.0.1:5000git@github.com:Xydra01/Gestalt.git
- Install the GitHub CLI:
brew install gh, thengh auth login(or setGH_TOKENin~/.bash_profilefor non-interactive use). - On macOS, this repo’s
.vscode/settings.jsonprepends/opt/homebrew/binto the integrated terminalPATHsoghis found after a new terminal tab. - If
ghis still missing (e.g. agent shells), run it via./scripts/ghfrom the repo root, which adds the same Homebrew paths. - If zsh does not load your token, run PR commands with Bash:
bash -lc 'cd …/Gestalt && ./scripts/gh pr create …'.
See gh pr create after pushing a branch.
MIT — LICENSE.