Public Access
docs: Sprint 13 — F9-lite (Ollama Cloud free-text plan synthesis) across all 6 running docs
Sprint 13 (commit bae9403) splits the Sprint 11 "Generate Meal
Plan" CTA into a 2-step modal: "Use the recipe library" (default,
Sprint 11 unchanged) or "Ask the LLM" (new). The LLM path POSTs
to /api/llm/plan; the backend calls kimi-k2.6:cloud on ollama.com,
parses the LLM’s JSON picks, creates a fresh plan, fills the
LLM’s picks, and falls through to the Sprint 6+ fillEmptySlots
pattern for the slots the LLM didn’t cover. No pre-existing WIP
files touched.
This commit updates the 6 running docs that track the sprint:
- .agent/plan.md — Sprint 13 section (S13.1-S13.4) added.
- .agent/context.md — Sprint 13 (D1-D9, Q1-Q3) added; file:line
references; key takeaways.
- Review/sprint13-verification.md — new file: 3-step browser
smoke + 4 API curls + a11y check + 6-risk table + future
work section.
- Review/ui-nielsen-audit.md — Sprint 13 status block (T7.1-T7.3)
at the top, after the Sprint 12 block.
- fix-ui-audit.md — Sprint 13 section (T7.1-T7.5) added after
the Sprint 12 section.
- Review/handoff-ui-audit.md — Batch I added to the deploy
instructions; Sprint 13 section added after Sprint 12; TL;DR
table row 13 added; Last-updated footer updated.
- docs/HANDOFF.md — Sprint 13 section added after the Sprint 12
section, with a path-forward paragraph for F9-full.
All 6 docs now reflect Sprint 13. §Future backlog remaining:
F9-full (local Ollama model pull on the host) — opt-in based on
cloud-billing feedback. _ask_llm is the single seam: F9-full only
needs to swap the URL + model name.
This commit is contained in:
@@ -21,14 +21,15 @@ If you are a new agent continuing this work, do this **in order**:
|
||||
- **Batch F:** Sprint 10 (one `git pull`, `docker compose up -d --build backend frontend` — no migration; the `NeverSuggest` table already exists from prior sprints).
|
||||
- **Batch G:** Sprint 11 (one `git pull`, `docker compose up -d --build frontend` — frontend-only, no migration, no backend rebuild).
|
||||
- **Batch H:** Sprint 12 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `recipe_search.py` router, frontend has the new toggle).
|
||||
4. **Open issues** in `.agent/plan.md` (the "Phase R1-R3" section is a prior plan; the **Sprint 12 active-sprint** section is the current state) and in `.agent/context.md` (decisions + open Qs for the current sprint).
|
||||
- **Batch I:** Sprint 13 (one `git pull`, `docker compose up -d --build backend frontend` — backend has the new `llm_plan.py` router, frontend has the new prompt modal).
|
||||
4. **Open issues** in `.agent/plan.md` (the "Phase R1-R3" section is a prior plan; the **Sprint 13 active-sprint** section is the current state) and in `.agent/context.md` (decisions + open Qs for the current sprint).
|
||||
5. **Do not** touch the pre-existing WIP files: `backend/app/api/recipes.py`, `backend/app/schemas/recipe.py`, `nginx/nginx.conf` (untouched since before this work; user's to manage).
|
||||
6. **When you commit,** use the `fix(ui):`, `feat(ui):`, `refactor(frontend):`, `docs(review):` Conventional Commit style. Force-add new files in `frontend/src/lib/` (the `.gitignore` line 17 `lib/` is a pre-existing bug that catches it).
|
||||
|
||||
**TL;DR of where things stand:**
|
||||
|
||||
- Sprints 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8: code committed and build green. Sprint 1 deployed. Sprints 2-8 awaiting user deploy.
|
||||
- The only remaining §Future item is F9 Ollama LLM matcher (proposal). F1 (onboarding) shipped as Sprint 9; the dead "Generate Meal Plan" CTA shipped as Sprint 11; F8 (Spoonacular) shipped as Sprint 12. All three are deployment-pending.
|
||||
- The §Future backlog is now empty: F1 (onboarding) shipped as Sprint 9; the dead "Generate Meal Plan" CTA shipped as Sprint 11; F8 (Spoonacular) shipped as Sprint 12; F9-lite shipped as Sprint 13. All four are deployment-pending. F9-full (local Ollama model pull) is the only remaining §Future item — opt-in based on cloud-billing feedback.
|
||||
- Pre-existing repo issues: 1 failing test (`test_filter_blocks_by_cost` — verified pre-Sprint 8), `.gitignore` `lib/` bug, no CI. Documented.
|
||||
|
||||
---
|
||||
@@ -54,7 +55,7 @@ If you are a new agent continuing this work, do this **in order**:
|
||||
|
||||
**Tracking docs:** `Review/sprint8-verification.md` (deploy + smoke), `Review/ui-nielsen-audit.md` Sprint 8 status block, `fix-ui-audit.md` T2.1–T2.10, this file, `docs/HANDOFF.md` Sprint 8 section.
|
||||
|
||||
**Thread 3 (§Future backlog) is deferred** until S8 is deployed + verified. F9 (Ollama) proposal remains. **Sprint 9 (F1 onboarding) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (wire the dead "Generate Meal Plan" CTA), and Sprint 12 (F8 Spoonacular search) are all committed 2026-06-05, awaiting user deploy.**
|
||||
**Thread 3 (§Future backlog) is deferred** until S8 is deployed + verified. F9-full (local Ollama model pull on the host) remains a future sprint. **Sprint 9 (F1 onboarding) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (wire the dead "Generate Meal Plan" CTA), Sprint 12 (F8 Spoonacular search), and Sprint 13 (F9-lite Ollama Cloud plan synthesis) are all committed 2026-06-05, awaiting user deploy.**
|
||||
|
||||
### Sprint 9 — F1 Onboarding Tour (H10)
|
||||
|
||||
@@ -118,6 +119,18 @@ If you are a new agent continuing this work, do this **in order**:
|
||||
|
||||
**Tracking docs:** `Review/sprint12-verification.md` (deploy + 4-step browser smoke + 2 API curls + quota test + a11y check + 5-risk table), `Review/ui-nielsen-audit.md` Sprint 12 status block, `fix-ui-audit.md` T6.1–T6.6, this file, `docs/HANDOFF.md` Sprint 12 section.
|
||||
|
||||
### Sprint 13 — F9-lite (Ollama Cloud plan synthesis) (§Future H10) (user-driven)
|
||||
|
||||
**Status: COMMITTED on 2026-06-05. Build green. Backend + frontend.** Awaiting user to `git pull` + `docker compose up -d --build backend frontend` (no migration).
|
||||
|
||||
**Root cause (one-liner):** the Sprint 11 "Generate Meal Plan" CTA was library-only. Sprint 13 splits it into a 2-step modal: "Use the recipe library" (Sprint 11 unchanged) or "Ask the LLM" (new). The LLM path lets the user describe what they want for the week ("Italian-inspired, vegetarian") and uses kimi-k2.6:cloud on ollama.com to pick meals from the local library.
|
||||
|
||||
**Scope (5 boxes):** NEW `backend/app/api/llm_plan.py` (~280 lines, 1 endpoint + 4 helpers + tolerance for malformed LLM responses), 2 schema additions (`LLMPlanRequest` + `LLMPlanResponse`), 1 router registration, 1 `mealPlannerApi.llm.plan` method, prompt modal in `Dashboard.tsx` (radio + textarea + click-outside-to-dismiss). **No new dependencies. No migration. No pre-existing WIP files touched.** Reuses `OLLAMA_BASE_URL` / `OLLAMA_API_KEY` / `OLLAMA_MODEL` from `config.py:36-38`.
|
||||
|
||||
**LLM tolerance:** a 60s timeout, parse-failure (markdown code fences, trailing commentary), or empty response all return 0 picks; the library fill takes over. The user sees a success toast with `picked_count: 0` — same as if the LLM had returned 0 picks — never a crash.
|
||||
|
||||
**Tracking docs:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table), `Review/ui-nielsen-audit.md` Sprint 13 status block, `fix-ui-audit.md` T7.1–T7.5, this file, `docs/HANDOFF.md` Sprint 13 section.
|
||||
|
||||
### Sprint 7 — Fix webui "empty meal plan" (date-semantics mismatch)
|
||||
|
||||
**Status: COMMITTED `09c7525` on 2026-06-05. Build green.** Awaiting user to `git pull` + run the SQL fix + rebuild.
|
||||
@@ -149,8 +162,9 @@ Twelve commits land all 14 audit findings + 6 §Future items + 2 user-driven spr
|
||||
| 10 | (committed 2026-06-05) | "Deny Forever" on Recipes — card overlay + RecipeDetail top bar + reason dropdown (allergy/dislike) + undo toast. New `POST`/`DELETE /api/never-suggest` (public) + `recipe_name` join. | ✅ green | ⚠️ committed; awaiting user deploy (backend + frontend, no migration) |
|
||||
| 11 | (committed 2026-06-05) | Wire the dead "Generate Meal Plan" empty-state CTA — `meals.create` + `meals.fillEmptySlots`; race-safe; reusable for F8/F9. | ✅ green | ⚠️ committed; awaiting user deploy (frontend-only) |
|
||||
| 12 | (committed 2026-06-05) | F8 Spoonacular search — "Search the web" toggle on `/recipes` + Import button. New `GET /api/recipes/search` + `POST /api/recipes/import`. Quota-gated (140pt/day). | ✅ green | ⚠️ committed; awaiting user deploy (backend + frontend) |
|
||||
| 13 | (committed 2026-06-05) | F9-lite Ollama Cloud plan synthesis — prompt modal on Dashboard CTA. New `POST /api/llm/plan`. Library-or-LLM radio; 60s timeout tolerance; library fills the rest. | ✅ green | ⚠️ committed; awaiting user deploy (backend + frontend) |
|
||||
|
||||
All work is on `main` ahead of `origin/main` (pre-existing WIP also present). All 12 sprints compile. **Sprint 1 is live. Sprints 2-12 are not yet live on `100.108.208.56:8082/`.**
|
||||
All work is on `main` ahead of `origin/main` (pre-existing WIP also present). All 13 sprints compile. **Sprint 1 is live. Sprints 2-13 are not yet live on `100.108.208.56:8082/`.**
|
||||
|
||||
**CRITICAL — Sprint 2 was effectively undeployable** because the CASE expression in `0015_normalize_pantry_aisles.py` failed with `text = boolean` on the `varchar(100) aisle` column. The bug is fixed in `d78bd18` (Sprint 5). Without that commit, `alembic upgrade head` would have failed on the deployment host, blocking Sprints 2, 3, 4 from going live. **The deployment host's DB still has the pre-0015 schema** — the migration must be run as part of the Sprints 2-5 batch deploy.
|
||||
|
||||
@@ -394,4 +408,4 @@ cd frontend && npm run build
|
||||
|
||||
Trust the build output. Trust the smoke checklist. Don't trust the deployment host's UI until the user confirms. The verification model is "I shipped, you verified, you reported, I fixed" — the agent in this role never sees the live UI directly.
|
||||
|
||||
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (Wire the dead "Generate Meal Plan" CTA), and Sprint 12 (F8 Spoonacular search) committed on 2026-06-05, awaiting user deploy**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
||||
**Last updated: 2026-06-05** — Sprint 1 deployed; Sprints 2-6 awaiting user deploy; **Sprint 7 (`09c7525`), Sprint 8 (`efd1fc6`), Sprint 9 (F1 Onboarding Tour) + post-deploy fix (`1562929`), Sprint 10 (Deny Forever on Recipes), Sprint 11 (Wire the dead "Generate Meal Plan" CTA), Sprint 12 (F8 Spoonacular search), and Sprint 13 (F9-lite Ollama Cloud plan synthesis) committed on 2026-06-05, awaiting user deploy**. See the "How to take over" and "Pending user deploy" sections at the top of this file.
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# Sprint 13 — F9-lite (Ollama Cloud free-text plan synthesis) — verification
|
||||
|
||||
**Status (2026-06-05):** ✅ Code complete. `npm run build` green. Awaiting user deploy.
|
||||
|
||||
## Summary
|
||||
|
||||
Sprint 13 splits the Sprint 11 "Generate Meal Plan" CTA into a 2-step modal: the user picks "Use the recipe library" (default, Sprint 11's existing flow) or "Ask the LLM" (new). The LLM path POSTs to `/api/llm/plan` with a free-text prompt; the backend calls `kimi-k2.6:cloud` on `ollama.com`, parses the LLM's JSON picks, creates a fresh plan, fills the LLM's picks, and falls through to the Sprint 6+ `fillEmptySlots` pattern for the slots the LLM didn't cover.
|
||||
|
||||
**No pre-existing WIP files touched.** Sprint 13 creates a new `backend/app/api/llm_plan.py` router (separate from the existing `llm_matcher.py` service) and reuses the established LLM call pattern (POST `${OLLAMA_BASE_URL}/chat/completions`, `Authorization: Bearer ${OLLAMA_API_KEY}`, same `max_tokens: 800, temperature: 0`, strip `<think>` blocks).
|
||||
|
||||
## Files changed
|
||||
|
||||
### Backend
|
||||
- **NEW** `backend/app/api/llm_plan.py` (~280 lines) — 1 endpoint + 4 helpers: `_ensure_ollama_configured`, `_serialize_library`, `_ask_llm` (mirrors `llm_matcher._ask_ollama`), `_parse_picks` (tolerant of markdown code fences), `_validate_picks`. 200-recipe cap on the library sent to the LLM.
|
||||
- **MODIFIED** `backend/app/schemas/__init__.py` — added `LLMPlanRequest` + `LLMPlanResponse`.
|
||||
- **MODIFIED** `backend/app/main.py` — registered `llm_plan_api.router` at `/api/llm`.
|
||||
|
||||
### Frontend
|
||||
- **MODIFIED** `frontend/src/api/index.ts` — added `llm.plan(data)`.
|
||||
- **MODIFIED** `frontend/src/pages/Dashboard.tsx` — added the prompt modal (radio for library vs. LLM + textarea for the LLM path) + extracted Sprint 11's body into `generateFromLibrary` + added `generateFromLLM`. New state: `showPromptModal`, `promptMode`, `promptText`, `promptBusy`. The CTA now opens the modal; the modal's submit button dispatches on the radio.
|
||||
|
||||
## Build verification
|
||||
|
||||
```text
|
||||
vite v5.4.21 building for production...
|
||||
transforming...
|
||||
✓ 1897 modules transformed.
|
||||
rendering chunks...
|
||||
computing gzip size...
|
||||
dist/index.html 0.54 kB │ gzip: 0.32 kB
|
||||
dist/assets/index-CPlRXkCg.css 42.38 kB │ gzip: 7.30 kB
|
||||
dist/assets/index-665b6KrF.js 503.82 kB │ gzip: 154.33 kB
|
||||
✓ built in 2.84s
|
||||
```
|
||||
|
||||
- `tsc` 0 errors, `vite` 0 errors.
|
||||
- Bundle: 500.28 → 503.82 kB (+3.5 kB for the modal + the LLM handler).
|
||||
- Backend AST clean on all 3 changed files.
|
||||
|
||||
## Backend verification (manual, post-deploy)
|
||||
|
||||
```bash
|
||||
# Requires OLLAMA_API_KEY + OLLAMA_BASE_URL + OLLAMA_MODEL set in the
|
||||
# backend env. (All three already exist; Sprint 13 just adds the
|
||||
# router that reads them.)
|
||||
|
||||
# 1) Happy path
|
||||
curl -sS -X POST 'http://100.108.208.56:8082/api/llm/plan' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Cookie: mealplanner_session=...' \
|
||||
-d '{"prompt": "Italian-inspired, vegetarian", "week_start": "2026-06-08"}' | jq
|
||||
|
||||
# Expected: { "plan_id": "<uuid>", "picked_count": 7+,
|
||||
# "filled_count": 14-, "failed_count": 0,
|
||||
# "reasoning": null }
|
||||
|
||||
# 2) OLLAMA_API_KEY unset (or temporarily unset in env + restart)
|
||||
curl -sS -X POST 'http://100.108.208.56:8082/api/llm/plan' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Cookie: mealplanner_session=...' \
|
||||
-d '{"prompt": "test", "week_start": "2026-06-08"}' -i | head -1
|
||||
# Expected: HTTP/1.1 503 Service Unavailable
|
||||
|
||||
# 3) Empty prompt
|
||||
curl -sS -X POST 'http://100.108.208.56:8082/api/llm/plan' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Cookie: mealplanner_session=...' \
|
||||
-d '{"prompt": "", "week_start": "2026-06-08"}' -i | head -1
|
||||
# Expected: HTTP/1.1 422 Unprocessable Entity
|
||||
|
||||
# 4) Plan for this week already exists
|
||||
curl -sS -X POST 'http://100.108.208.56:8082/api/llm/plan' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Cookie: mealplanner_session=...' \
|
||||
-d '{"prompt": "test", "week_start": "2026-06-01"}' -i | head -1
|
||||
# Expected: HTTP/1.1 400 Bad Request (if a plan for 2026-06-01 exists)
|
||||
```
|
||||
|
||||
## Browser smoke (3 steps)
|
||||
|
||||
Run on `http://100.108.208.56:8082/`.
|
||||
|
||||
1. **Land on `/` with no plan.** Click "Generate Meal Plan". Confirm: the modal opens with the title "Generate Meal Plan", a short description, and the 2 radio options ("Use the recipe library" selected by default, "Ask the LLM" unselected). The "Generate" button is enabled; the "Cancel" button is enabled.
|
||||
2. **Type "Italian-inspired, vegetarian" + select "Ask the LLM" + click Generate.** Confirm: button label flips to "Asking LLM…" (with a spinning Loader2 icon), the modal is non-dismissible, ~5-15s later (depends on the LLM latency) the modal closes, the plan grid renders with the LLM's picks, and a toast shows `"Planned N meals (LLM picked K, library filled the rest)"`.
|
||||
3. **Refresh the page.** Confirm the plan persists. The empty state does NOT re-appear.
|
||||
|
||||
## A11y check
|
||||
|
||||
- The modal is a `<div className="fixed inset-0 z-50">` with a click-outside-to-dismiss handler (skipped while `promptBusy` is true).
|
||||
- The radios are real `<input type="radio">` with associated `<label>` blocks. Tab cycles through both radios, the textarea (when LLM mode is selected), and the 2 buttons.
|
||||
- The textarea has `autoFocus` when the modal opens in LLM mode. Default LLM mode is library (no textarea shown); switching to LLM mode does not steal focus (intentional — the user just clicked the radio, they shouldn't lose their cursor).
|
||||
- The character counter is a `<div>` with `text-right` alignment, screen-reader-accessible via the `500` max length on the textarea.
|
||||
- The "Generate" button is `disabled` while `promptBusy` is true (matches Sprint 11's `generatingFirstPlan` state pattern).
|
||||
- The Sprint 9 OnboardingTour's first step is the Dashboard's Weekly Overview card; the modal renders on top of that card via `z-50` + `bg-black/40` scrim. The tour is not affected.
|
||||
|
||||
## Risks & mitigations
|
||||
|
||||
- **R1: LLM returns 0 picks or 21 identical recipes.** Mitigation: the response is validated (recipe_id in library, day_of_week 1-7, meal_type in {breakfast, lunch, dinner}) and invalid entries are dropped. The library fill then takes over for any slot the LLM didn't cover. A prompt that returns 0 valid picks still produces a complete plan from the library.
|
||||
- **R2: LLM times out (60s).** Mitigation: the `requests.post` call has a 60s timeout. On timeout, `_ask_llm` returns None; the endpoint then calls the library fill (no LLM picks). The user sees a success toast with `picked_count: 0`.
|
||||
- **R3: Library is empty.** Mitigation: 400 with `detail: "recipe library is empty; import some recipes first"`. The frontend `showApiError` surfaces the message. (Pre-existing WIP, also affects the Sprint 11 library path.)
|
||||
- **R4: OLLAMA_API_KEY unset.** Mitigation: 503 with clear `detail: "OLLAMA_API_KEY not configured; set it in the backend env"`. The frontend `showApiError` surfaces the message; the user can switch to "Use the recipe library" and proceed.
|
||||
- **R5: OLLAMA_CLOUD costs.** The operator's existing OLLAMA billing applies. The endpoint is public + requires `require_session` (so no public abuse). A future sprint could add a per-day rate limit.
|
||||
- **R6: Sprint 12's pre-existing WIP collision.** Verified: the WIP `recipes.py` is registered at `/api/recipes/*` only; my new `/api/llm/plan` is in a different prefix. No collision.
|
||||
|
||||
## Commit
|
||||
|
||||
One commit: `feat(ui): Sprint 13 — F9-lite (Ollama Cloud free-text plan synthesis)`. Files:
|
||||
|
||||
- `backend/app/api/llm_plan.py` (NEW, ~280 lines)
|
||||
- `backend/app/schemas/__init__.py` (2 Pydantic models)
|
||||
- `backend/app/main.py` (router registration)
|
||||
- `frontend/src/api/index.ts` (1 new method)
|
||||
- `frontend/src/pages/Dashboard.tsx` (modal + LLM handler + extracted library handler)
|
||||
|
||||
## Future work (NOT in Sprint 13)
|
||||
|
||||
- **F9-full — local Ollama model pull.** Pull Mistral 7B or Llama 3 8B (~4 GB) on the host + a separate `ollama serve` process. Avoids cloud costs and the LLM_API_KEY dependency. Future sprint.
|
||||
- **Prompt template library.** Save common prompts ("easy weeknight", "vegetarian week", "quick breakfasts") as one-click buttons. Out of scope for the initial ship.
|
||||
- **Multi-week plans.** One week at a time. The endpoint is parameterised on `week_start`; calling it twice with two different weeks would work, but the UI doesn't surface it yet.
|
||||
- **Streaming the LLM response.** Currently waits for the full response. Future sprint could use the Ollama streaming API + a "thinking…" indicator.
|
||||
- **Per-day rate limit.** A future sprint could gate the LLM call on a per-day budget to avoid surprise cloud bills.
|
||||
- **Vitest unit test for `useOnboarding`** (Q4 from Sprint 9). Still pending.
|
||||
@@ -131,6 +131,12 @@ The app looks polished on the surface (Tailwind palette, clean cards, working to
|
||||
> - **T6.4** Frontend: `frontend/src/api/index.ts` adds `recipes.search` + `recipes.importRecipe` + 3 stub methods (`recommended`, `listIngredients`, `createIngredient`) to satisfy pre-existing call sites that were previously hidden by a smaller API surface. `frontend/src/pages/Recipes.tsx` adds the toggle button (with `aria-pressed`) + the web-search panel (`<div role="region" aria-label="Web recipe search" aria-busy={webLoading}>`) + the import mutation (toast on success, `showApiError` on failure). `frontend/src/types/index.ts` adds optional `ingredient` + `is_optional` to `RecipeIngredient` for pre-existing MealDetail.tsx call sites.
|
||||
> - **T6.5** Pre-existing tsc errors exposed by the API surface expansion (5 errors in Pantry/MealDetail/Recommended.tsx) — resolved per user decision: added 5 stub API methods + 2 type fields. Documented in `Review/sprint12-verification.md` D-fix section.
|
||||
> - **Verification log:** `Review/sprint12-verification.md` (deploy + 4-step browser smoke + 2 API curls + quota test + a11y check + 5-risk table). Deploy is `git pull` + `docker compose up -d --build backend frontend` (backend has the new router; frontend has the new toggle).
|
||||
>
|
||||
> **Sprint 13 status (committed 2026-06-05, awaiting deploy):** F9-lite — splits the Sprint 11 "Generate Meal Plan" CTA into a 2-step modal: the user picks "Use the recipe library" (default, Sprint 11's flow) or "Ask the LLM" (new). The LLM path POSTs to `/api/llm/plan`; the backend calls `kimi-k2.6:cloud` on `ollama.com`, parses the LLM's JSON picks, creates a fresh plan, fills the LLM's picks, and falls through to the Sprint 6+ `fillEmptySlots` pattern for the slots the LLM didn't cover. **No pre-existing WIP files touched.** F9-full (local Ollama model pull on the host) remains a future sprint.
|
||||
> - **T7.1** `backend/app/api/llm_plan.py` (NEW, ~280 lines). 1 endpoint (`POST /api/llm/plan` body `{prompt, week_start}`) + 4 helpers (`_ensure_ollama_configured`, `_serialize_library` with a 200-recipe cap, `_ask_llm` mirroring the `llm_matcher._ask_ollama` pattern, `_parse_picks` tolerant of markdown code fences, `_validate_picks` that drops invalid entries). 60s timeout, 422 on empty/oversized prompt, 503 on missing OLLAMA_API_KEY, 400 on duplicate week.
|
||||
> - **T7.2** `backend/app/schemas/__init__.py` — added `LLMPlanRequest` + `LLMPlanResponse` Pydantic models. The router is registered in `main.py:65-66` at the `/api/llm` prefix.
|
||||
> - **T7.3** Frontend: `frontend/src/api/index.ts` adds `llm.plan(data)`. `frontend/src/pages/Dashboard.tsx` adds the prompt modal (radio for library vs. LLM + textarea for the LLM path with 500-char counter) + extracted Sprint 11's body into `generateFromLibrary` + added `generateFromLLM`. New state: `showPromptModal`, `promptMode`, `promptText`, `promptBusy`. Click-outside-to-dismiss is disabled while `promptBusy` is true. The textarea `autoFocus`es when LLM mode is selected.
|
||||
> - **Verification log:** `Review/sprint13-verification.md` (deploy + 3-step browser smoke + 4 API curls + a11y check + 6-risk table). Deploy is `git pull` + `docker compose up -d --build backend frontend` (no migration, no new dependencies).
|
||||
> - **No new dependencies. No migration. Admin path unchanged.**
|
||||
>
|
||||
> **Sprint 6 status (commit `8ad4ef6`, awaiting deploy):** Two §Future items, both with design decisions captured in the commit message.
|
||||
|
||||
Reference in New Issue
Block a user