feat(api): expand routines tool-calling to reduce prompt load
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.
This commit is contained in:
parent
cfd2485b7e
commit
558708653c
3 changed files with 329 additions and 47 deletions
|
|
@ -250,6 +250,9 @@ def test_suggest_routine(client, session):
|
|||
kwargs = mock_gemini.call_args.kwargs
|
||||
assert "function_handlers" in kwargs
|
||||
assert "get_product_inci" in kwargs["function_handlers"]
|
||||
assert "get_product_safety_rules" in kwargs["function_handlers"]
|
||||
assert "get_product_actives" in kwargs["function_handlers"]
|
||||
assert "get_product_usage_notes" in kwargs["function_handlers"]
|
||||
|
||||
|
||||
def test_suggest_batch(client, session):
|
||||
|
|
|
|||
|
|
@ -4,15 +4,20 @@ from datetime import date, timedelta
|
|||
from sqlmodel import Session
|
||||
|
||||
from innercontext.api.routines import (
|
||||
_build_actives_tool_handler,
|
||||
_build_day_context,
|
||||
_build_grooming_context,
|
||||
_build_inci_tool_handler,
|
||||
_build_objectives_context,
|
||||
_build_products_context,
|
||||
_build_recent_history,
|
||||
_build_safety_rules_tool_handler,
|
||||
_build_skin_context,
|
||||
_build_usage_notes_tool_handler,
|
||||
_contains_minoxidil_text,
|
||||
_ev,
|
||||
_extract_active_names,
|
||||
_extract_requested_product_ids,
|
||||
_get_available_products,
|
||||
_is_minoxidil_product,
|
||||
)
|
||||
|
|
@ -336,3 +341,68 @@ def test_build_inci_tool_handler_returns_only_available_ids(session: Session):
|
|||
assert products[0]["id"] == str(available.id)
|
||||
assert products[0]["name"] == "Available"
|
||||
assert products[0]["inci"] == ["Water", "Niacinamide"]
|
||||
|
||||
|
||||
def test_extract_requested_product_ids_dedupes_and_limits():
|
||||
ids = _extract_requested_product_ids(
|
||||
{
|
||||
"product_ids": [
|
||||
"id-1",
|
||||
"id-2",
|
||||
"id-1",
|
||||
3,
|
||||
"id-3",
|
||||
"id-4",
|
||||
]
|
||||
},
|
||||
max_ids=3,
|
||||
)
|
||||
assert ids == ["id-1", "id-2", "id-3"]
|
||||
|
||||
|
||||
def test_extract_active_names_uses_compact_distinct_names(session: Session):
|
||||
p = Product(
|
||||
id=uuid.uuid4(),
|
||||
name="Test",
|
||||
category="serum",
|
||||
brand="Test",
|
||||
recommended_time="both",
|
||||
leave_on=True,
|
||||
actives=[
|
||||
{"name": "Niacinamide", "percent": 10},
|
||||
{"name": "Niacinamide", "percent": 5},
|
||||
{"name": "Zinc PCA", "percent": 1},
|
||||
],
|
||||
product_effect_profile={},
|
||||
)
|
||||
|
||||
names = _extract_active_names(p)
|
||||
assert names == ["Niacinamide", "Zinc PCA"]
|
||||
|
||||
|
||||
def test_additional_tool_handlers_return_product_payloads(session: Session):
|
||||
p = Product(
|
||||
id=uuid.uuid4(),
|
||||
name="Detail Product",
|
||||
category="serum",
|
||||
brand="Test",
|
||||
recommended_time="both",
|
||||
leave_on=True,
|
||||
usage_notes="Apply morning and evening.",
|
||||
actives=[{"name": "Niacinamide", "percent": 5, "functions": ["niacinamide"]}],
|
||||
incompatible_with=[{"target": "Retinol", "scope": "same_step"}],
|
||||
context_rules={"safe_after_shaving": True},
|
||||
product_effect_profile={},
|
||||
)
|
||||
|
||||
ids_payload = {"product_ids": [str(p.id)]}
|
||||
|
||||
actives_out = _build_actives_tool_handler([p])(ids_payload)
|
||||
assert actives_out["products"][0]["actives"][0]["name"] == "Niacinamide"
|
||||
|
||||
notes_out = _build_usage_notes_tool_handler([p])(ids_payload)
|
||||
assert notes_out["products"][0]["usage_notes"] == "Apply morning and evening."
|
||||
|
||||
safety_out = _build_safety_rules_tool_handler([p])(ids_payload)
|
||||
assert "incompatible_with" in safety_out["products"][0]
|
||||
assert "context_rules" in safety_out["products"][0]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue