The field name 'metadata' conflicts with Pydantic's internal ClassVar.
Renamed to 'response_metadata' throughout:
- Backend: RoutineSuggestion, BatchSuggestion, ShoppingSuggestionResponse
- Frontend: TypeScript types and component usages
This fixes the AttributeError when setting metadata on SQLModel instances.
Components created:
- ValidationWarningsAlert: Display validation warnings with collapsible list
- StructuredErrorDisplay: Parse and display HTTP 502 errors as bullet points
- AutoFixBadge: Show automatically applied fixes
- ReasoningChainViewer: Collapsible panel for LLM thinking process
- MetadataDebugPanel: Collapsible debug info (model, duration, token metrics)
CSS changes:
- Add .editorial-alert--warning and .editorial-alert--info variants
Integration:
- Update routines/suggest page to show warnings, auto-fixes, reasoning, and metadata
- Update products/suggest page with same observability components
- Replace plain error divs with StructuredErrorDisplay for better UX
All components follow design system and pass svelte-check with 0 errors
Backend changes:
- Create ResponseMetadata and TokenMetrics models for API responses
- Modify call_gemini() and call_gemini_with_function_tools() to return (response, log_id) tuple
- Add _build_response_metadata() helper to extract metadata from AICallLog
- Update routines API (/suggest, /suggest-batch) to populate validation_warnings, auto_fixes_applied, and metadata
- Update products API (/suggest) to populate observability fields
- Update skincare API to handle new return signature
Frontend changes:
- Add TypeScript types: TokenMetrics, ResponseMetadata
- Update RoutineSuggestion, BatchSuggestion, ShoppingSuggestionResponse with observability fields
Next: Create UI components to display warnings, reasoning chains, and token metrics
Move product and skin AI helpers into modal flows, simplify product edit/inventory navigation, and improve responsive actions so core forms are faster to use. Localize remaining frontend labels/placeholders and strip deprecated Rollup output options to remove deploy-time build warnings.
- Install eslint, prettier and related plugins
- Add lint and format npm scripts
- Configure eslint.config.js with Svelte + TypeScript rules
- Configure .prettierrc with Svelte plugin
- Fix code to comply with lint rules:
- Use resolve() for navigation links
- Use SvelteMap for reactive maps
- Use writable instead of +
- Remove unused imports and variables
Note: ignoreGoto is set to true due to eslint-plugin-svelte#1327
- Add POST /api/products/suggest endpoint that analyzes skin condition
and inventory to suggest product types (e.g., 'Salicylic Acid 2% Masque')
- Add MCP tool get_shopping_suggestions() for MCP clients
- Add 'Suggest' button to Products page in frontend
- Add /products/suggest page with suggestion cards
- Include product type, key ingredients, target concerns, why_needed,
recommended_time, and frequency in suggestions
- Fix stock logic: sealed products now count as available inventory
- Add legend to clarify ✓ (in stock) vs ✗ (not in stock) markers
- Layout: mobile hamburger + drawer nav (backdrop button + sibling nav),
desktop sidebar hidden on small screens, p-4 md:p-8 main padding
- Products: card list view on mobile, flex-wrap filters
- Lab results: card list view on mobile
- ProductForm: responsive grids (grid-cols-1 sm:grid-cols-2), skin
profile checkboxes 2→3 cols, active ingredient row restructured
(name+✕ in flex row, percent/strength/irritation in 3-col grid),
section headers stack on mobile
- Skin snapshots: date+icons on one row, badges on separate row below
- Product [id] header: back link stacked above title, redundant badge removed
- Routines header: flex-col on mobile, sm:flex-row
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove _build_inventory_context; fold pao_months into DOSTĘPNE PRODUKTY entries
- Remove "Otwarte równolegle" duplicate section from prompt
- Rename OSTATNIE RUTYNY (7 dni) → OSTATNIE RUTYNY
- Add _build_day_context and SuggestRoutineRequest.leaving_home (optional bool)
- System prompt: replace unconditional PAO rule with conditional; add SPF factor
selection logic based on KONTEKST DNIA leaving_home value
- Frontend: leaving_home checkbox (AM only) + i18n keys pl/en
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
analyzeSkinPhotos was hardcoding PUBLIC_API_BASE, causing browser-side
requests to hit localhost:8000 directly instead of going through nginx.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
priorities was present in the model, API, and LLM prompt but never
surfaced in the frontend — add it to create/edit forms, read view,
AI photo pre-fill, and page.server.ts actions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add include_minoxidil_beard flag to SuggestRoutineRequest and SuggestBatchRequest
- Detect minoxidil products by scanning name, brand, INCI and actives; pass them
to the LLM even though they are medications
- Inject CELE UŻYTKOWNIKA context block into prompts when flag is enabled
- Add _build_objectives_context() returning empty string when flag is off
- Add call_gemini() helper that centralises Gemini API calls and logs every
request/response to a new ai_call_logs table (AICallLog model + /ai-logs router)
- Nginx: raise client_max_body_size to 16 MB for photo uploads
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Products in the Add/Edit step dropdowns are now grouped by category
(Cleanser → Serum → Moisturizer → …) using SelectGroup/SelectGroupHeading,
and sorted alphabetically by brand then name within each group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Step numbers now use each-block index (i+1) instead of order_index+1,
fixing display when order_index starts from 1 in existing data
- api.ts: browser-side requests use /api (nginx proxy) instead of
PUBLIC_API_BASE (localhost:8000), fixing PATCH/client calls on remote hosts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Install svelte-dnd-action v0.9.69
- Use dragHandleZone + dragHandle for per-step ⋮⋮ drag handles
- PATCH only steps whose order_index changed after a drop
- Inline edit mode (✎ button) expands step in-place: product steps show product/dose/region selects; action steps show action_type/notes
- DnD disabled while a step is being edited
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Install @inlang/paraglide-js v2 with Vite plugin and paraglideMiddleware hook
- Add messages/pl.json and messages/en.json with ~400 translation keys
- Create project.inlang/settings.json (PL as base locale)
- Add LanguageSwitcher component (cookie-based, no URL prefix needed)
- Replace all hardcoded strings across 14 pages/components with m.*() calls
- ProductForm uses derived label maps for all enum types (category, texture, etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Steps were numbered from 2 due to off-by-one (order_index + 1).
Product steps showed "Unknown step" because the template checked
step.product (never returned by API) instead of looking up by
step.product_id in the already-loaded products list.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Make Routine.steps optional in types.ts — list endpoint does not
eager-load steps, so the field is absent from the JSON response
- Guard routine.steps?.length ?? 0 in routines list page to prevent
SSR TypeError when steps is undefined
- Move redirect() outside try/catch in suggest save action; SvelteKit
redirect throws internally and was being caught, causing a 500 error
instead of navigating to the new routine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Gemini-powered endpoints and frontend pages for proposing skincare
routines based on skin state, product compatibility, grooming schedule,
and recent history.
Backend (routines.py):
- POST /routines/suggest — single AM/PM routine for a date
- POST /routines/suggest-batch — AM+PM plan for up to 14 days
- Prompt context: skin snapshot, grooming schedule, 7-day history,
filtered product list with effects/incompatibilities/context rules
- Respects retinoid frequency limits, acid/retinoid separation,
grooming-aware safe_after_shaving rules
Frontend:
- /routines/suggest page with tab switcher (single / batch)
- Single tab: date + AM/PM + optional notes → generate → preview → save
- Batch tab: date range + notes → collapsible day cards (AM+PM) → save all
- Loading spinner during Gemini calls; product names resolved from map
- "Zaproponuj rutynę AI" button added to routines list page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New route /routines/grooming-schedule with load + create/update/delete actions
- Entries grouped by day of week with inline editing
- 4 API functions added to api.ts (get/create/update/delete)
- Nav: add Grooming link, fix isActive to not highlight parent when child matches
- Nav: replace ⌂ text char with 🏠 emoji for Dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace category filter dropdown with client-side grouping and a
3-way ownership toggle (All / Owned / Not owned). Products are grouped
by category with header rows as visual dividers, sorted brand → name
within each group. Category column removed (redundant with headings).
Backend: GET /products now returns ProductWithInventory so inventory
data is available for ownership filtering (bulk-loaded in one query).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove s.trend from dashboard (field removed from SkinConditionSnapshot)
- Replace trend with texture in SkinPhotoAnalysisResponse (api.ts)
- Use record_id instead of id for LabResult and MedicationEntry keyed each blocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the derived `trend` field (better computed from history by the MCP
agent) and add `texture: smooth|rough|flaky|bumpy` which LLM can reliably
assess from photos. Updates model, API, system prompt, tests, and frontend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the All/AM/PM filter buttons and part_of_day param from
the routines list page — date-based sorting/filtering is preferred.
Add missing keyed {#each} blocks across all pages to follow
Svelte 5 best practice (dashboard, products, medications,
lab results, routines list and detail, skin).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add POST /skincare/analyze-photos endpoint that accepts 1–3 skin
photos, sends them to Gemini vision, and returns a structured
SkinPhotoAnalysisResponse for pre-filling the snapshot form.
Extract shared Gemini client setup into innercontext/llm.py
(get_gemini_client) so both products and skincare use a single
default model (gemini-flash-latest) and API key check.
Frontend: AI photo card on /skin page with file picker, previews,
and auto-fill of all form fields from the analysis result.
New fields (skin_type, sebum_tzone, sebum_cheeks) added to form
and server action.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously redirected to /products/{id} (edit page) which looks
identical to the create form, making it appear as if nothing happened.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add POST /products/parse-text endpoint that accepts raw product text,
calls Gemini (google-genai) with a structured extraction prompt, and
returns a partial ProductParseResponse. Frontend gains a collapsible
"AI pre-fill" card at the top of ProductForm that merges the LLM
response into all form fields reactively.
- Backend: ProductParseRequest/Response schemas, system prompt with
enum constraints, temperature=0.0 for deterministic extraction,
effect_profile always returned in full
- Frontend: parseProductText() in api.ts; controlled $state bindings
for all text/number/checkbox inputs; applyAiResult() merges response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap all prop-derived $state initializers with untrack() to explicitly
signal that one-time capture from the product prop is intentional.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds full_weight_g and empty_weight_g to ProductBase (inherited by Product
and response models) so per-product package weight specs are captured.
Adds last_weighed_at to ProductInventory to record when a package was last
weighed. Wires up all fields through API schemas, frontend types, forms, and
the product detail page (add/edit/display).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>