feat(meal-planner): structured output for add_meal_to_meal_plan (v0.11.3)

Map verified mpcreate response to same field layout as get_meal_plan.
Key difference: a00.r.r is an array (take [0]) unlike mpcreateByRecipeId
which returns a plain object. is_from_recipe_box is always false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-17 12:24:48 +02:00
parent 0ed9d62e4a
commit a26a637c83
6 changed files with 54 additions and 10 deletions
+31 -2
View File
@@ -2430,8 +2430,37 @@ def add_meal_to_meal_plan(
except RuntimeError as exc:
return f"Error: {exc}"
# Return raw response until the structure is verified in production.
return json.dumps(data, ensure_ascii=False, indent=2)
# NOTE: mpcreate returns a00.r.r as an *array*, unlike mpcreateByRecipeId
# which returns a plain object. Take the first (and only) element.
try:
items = data["a00"]["r"]["r"]
if not isinstance(items, list) or not items:
raise TypeError("a00.r.r is not a non-empty list")
dish = items[0]
if not isinstance(dish, dict) or "metaId" not in dish:
raise TypeError("unexpected dish shape")
except (KeyError, TypeError):
return json.dumps(
{"warning": "Unexpected mpcreate response structure", "raw": data},
ensure_ascii=False,
indent=2,
)
rights = dish.get("rights") or {}
result: dict[str, Any] = {
"id": dish.get("metaId"),
"date": dish.get("date"),
"type": dish.get("type"),
"name": dish.get("name"),
"recipe_id": dish.get("recipeId") or None,
# mpcreate always creates free-text entries (not from the recipe box).
"is_from_recipe_box": False,
"note": None,
"serves": None,
"can_update": rights.get("canUpdate") == "true",
"can_delete": rights.get("canDelete") == "true",
}
return json.dumps(result, ensure_ascii=False, indent=2)
# ---------------------------------------------------------------------------