Two critical bugs identified from production logs:
1. UUID Mismatch Bug (0 products returned from function tools):
- Context shows 8-char short IDs: '63278801'
- Function handler expected full UUIDs: '63278801-xxxx-...'
- LLM requested short IDs, handler couldn't match → 0 products
Fix: Index products by BOTH full UUID and short ID (first 8 chars)
in build_product_details_tool_handler. Accept either format.
Added deduplication to handle duplicate requests.
Maintains Phase 2 token optimization (no context changes).
2. MAX_TOKENS Error (response truncation):
- max_output_tokens=4096 includes thinking tokens (~3500)
- Only ~500 tokens left for JSON response
- MEDIUM thinking level (Phase 2) consumed budget
Fix: Increase max_output_tokens from 4096 → 8192 across all
creative endpoints (routines/suggest, routines/suggest-batch,
products/suggest). Updated default in get_creative_config().
Gives headroom: ~3500 thinking + ~4500 response = ~8000 total
From production logs (ai_call_logs):
- Log 71699654: Success but response_text null (function call only)
- Log 2db37c0f: MAX_TOKENS failure, tool returned 0 products
Both issues now resolved.
When products are loaded from PostgreSQL, JSON columns (effect_profile,
context_rules) are deserialized as plain dicts, not Pydantic models.
The build_product_context_summary function was accessing these fields
as object attributes (.safe_with_compromised_barrier) which caused:
AttributeError: 'dict' object has no attribute 'safe_with_compromised_barrier'
Fix: Add isinstance(dict) checks like build_product_context_detailed already does.
Handle both dict (from DB) and object (from Pydantic) cases.
Traceback from production:
File "llm_context.py", line 91, in build_product_context_summary
if product.context_rules.safe_with_compromised_barrier:
AttributeError: 'dict' object has no attribute...
- Add tiered context system (summary/detailed/full) to reduce token usage by 70-80%
- Replace old _build_products_context with build_products_context_summary_list (Tier 1: ~15 tokens/product vs 150)
- Optimize function tool responses: exclude INCI list by default (saves ~15KB/product)
- Reduce actives from 24 to top 5 in function tools
- Add reasoning_chain field to AICallLog model for observability
- Implement _extract_thinking_content to capture LLM reasoning (MEDIUM thinking level)
- Strengthen prompt enforcement for prohibited fields (dose, amount, quantity)
- Update get_creative_config to use MEDIUM thinking level instead of LOW
Token Savings:
- Routine suggestions: 9,613 → ~1,300 tokens (-86%)
- Batch planning: 12,580 → ~1,800 tokens (-86%)
- Function tool responses: ~15KB → ~2KB per product (-87%)
Breaks discovered in log analysis (ai_call_log.json):
- Lines 10, 27, 61, 78: LLM returned prohibited dose field
- Line 85: MAX_TOKENS failure (output truncated)
Phase 2 complete. Next: two-phase batch planning with safety verification.
Two bugs in /routines/suggest where the LLM could override hard constraints:
1. Products with min_interval_hours (e.g. retinol at 72h) were passed to
the LLM even if used too recently. The LLM reasoned away the constraint
in at least one observed case. Fix: added _filter_products_by_interval()
which removes ineligible products before the prompt is built, so they
don't appear in AVAILABLE PRODUCTS at all.
2. Minoxidil was included in the available products list regardless of the
include_minoxidil_beard flag. Only the objectives context was gated,
leaving the product visible to the LLM which would include it based on
recent usage history. Fix: added include_minoxidil param to
_get_available_products() and threaded it through suggest_routine and
suggest_batch.
Also refactored _build_products_context() to accept a pre-supplied
products list instead of calling _get_available_products() internally,
ensuring the tool handler and context text always use the same filtered set.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
Keep /products/suggest lean by exposing product UUIDs and fetching INCI, safety rules, actives, and usage notes on demand through Gemini function tools. Add conservative fallback behavior for tool roundtrip limits and expand helper tests to cover tool wiring and payload handlers.
Keep the /routines/suggest base context lean by sending only active names and fetching detailed safety, actives, usage notes, and INCI on demand. Add a conservative fallback when tool roundtrip limits are hit to preserve safe outputs instead of failing the request.
Enable on-demand INCI retrieval in /routines/suggest through Gemini function calling so detailed ingredient data is fetched only when needed. Persist and normalize tool_trace data in AI logs to make function-call behavior directly inspectable via /ai-logs endpoints.
Expose leave-on behavior, contraindications, safety alerts, and compact usage notes in AVAILABLE PRODUCTS so Gemini can make safer routine decisions with real-world product constraints.
Introduces `get_extraction_config` and `get_creative_config` to standardize Gemini API calls.
* Defines explicit config profiles with appropriate `temperature` and `thinking_level` for Gemini 3 Flash.
* Extraction tasks use minimal thinking and temp=0.0 to reduce latency and token usage.
* Creative tasks use low thinking, temp=0.4, and top_p=0.8 to balance naturalness and safety.
* Applies these helpers across products, routines, and skincare endpoints.
* Also updates default model to `gemini-3-flash-preview`.
- 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