added fitness
This commit is contained in:
@@ -3,6 +3,10 @@ from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.fitness.service import FitnessService
|
||||
from app.fitness.structuring import structure_meal, structure_workout
|
||||
from app.integrations.openfoodfacts import OpenFoodFactsClient
|
||||
from app.integrations.wger import WgerClient
|
||||
from app.memory.service import MemoryService
|
||||
from app.pomodoro.service import PomodoroService
|
||||
from app.projects.service import ProjectService
|
||||
@@ -268,6 +272,164 @@ TOOL_DEFINITIONS: list[dict[str, Any]] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_fitness_summary",
|
||||
"description": "Сводка фитнеса за сегодня: ккал, БЖУ, вода, тренировки.",
|
||||
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "set_fitness_profile",
|
||||
"description": "Настроить фитнес-профиль и пересчитать цели ккал/БЖУ/воды.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sex": {"type": "string", "description": "male/female"},
|
||||
"age": {"type": "integer"},
|
||||
"height_cm": {"type": "number"},
|
||||
"weight_kg": {"type": "number"},
|
||||
"activity_level": {
|
||||
"type": "string",
|
||||
"description": "sedentary/light/moderate/active/very_active",
|
||||
},
|
||||
"goal": {"type": "string", "description": "lose/maintain/gain"},
|
||||
"target_weight_kg": {"type": "number"},
|
||||
"weekly_workouts": {"type": "integer"},
|
||||
},
|
||||
"required": [],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calc_fitness_targets",
|
||||
"description": "Калькулятор BMR/TDEE/макросов без сохранения.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sex": {"type": "string"},
|
||||
"age": {"type": "integer"},
|
||||
"height_cm": {"type": "number"},
|
||||
"weight_kg": {"type": "number"},
|
||||
"activity_level": {"type": "string"},
|
||||
"goal": {"type": "string"},
|
||||
},
|
||||
"required": ["weight_kg", "height_cm", "age"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "log_meal",
|
||||
"description": "Записать приём пищи. LLM оценит ккал и БЖУ из текста.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string", "description": "Что съел"},
|
||||
"meal_type": {"type": "string"},
|
||||
},
|
||||
"required": ["text"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "log_water",
|
||||
"description": "Записать воду в мл.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"amount_ml": {"type": "integer"},
|
||||
},
|
||||
"required": ["amount_ml"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "log_weight",
|
||||
"description": "Записать вес в кг.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"weight_kg": {"type": "number"},
|
||||
"body_fat_pct": {"type": "number"},
|
||||
"notes": {"type": "string"},
|
||||
},
|
||||
"required": ["weight_kg"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "log_workout",
|
||||
"description": "Записать тренировку из текста.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": {"type": "string"},
|
||||
},
|
||||
"required": ["text"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "lookup_food",
|
||||
"description": "Поиск продукта в Open Food Facts (ккал на 100г).",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string"},
|
||||
"limit": {"type": "integer"},
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "lookup_exercise",
|
||||
"description": "Поиск упражнения в базе wger.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {"type": "string"},
|
||||
"limit": {"type": "integer"},
|
||||
},
|
||||
"required": ["query"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "set_fitness_reminder",
|
||||
"description": "Вкл/выкл или настроить напоминание: water, meal, workout, weigh_in.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {"type": "string"},
|
||||
"enabled": {"type": "boolean"},
|
||||
"hour": {"type": "integer"},
|
||||
"minute": {"type": "integer"},
|
||||
"interval_hours": {"type": "integer"},
|
||||
},
|
||||
"required": ["kind"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
@@ -299,6 +461,7 @@ async def execute_tool(
|
||||
pomodoro = PomodoroService(db)
|
||||
projects = ProjectService(db)
|
||||
memory = MemoryService(db)
|
||||
fitness = FitnessService(db)
|
||||
|
||||
try:
|
||||
if name == "get_pomodoro_status":
|
||||
@@ -370,6 +533,66 @@ async def execute_tool(
|
||||
int(arguments["session_id"]),
|
||||
arguments.get("summary", ""),
|
||||
)
|
||||
elif name == "get_fitness_summary":
|
||||
result = fitness.get_daily_summary()
|
||||
elif name == "set_fitness_profile":
|
||||
updates = {
|
||||
k: arguments[k]
|
||||
for k in (
|
||||
"sex", "age", "height_cm", "weight_kg", "activity_level",
|
||||
"goal", "target_weight_kg", "weekly_workouts",
|
||||
)
|
||||
if k in arguments and arguments[k] is not None
|
||||
}
|
||||
result = fitness.set_profile(updates)
|
||||
elif name == "calc_fitness_targets":
|
||||
result = fitness.calc_targets(arguments)
|
||||
elif name == "log_meal":
|
||||
structured = await structure_meal(arguments.get("text", ""))
|
||||
result = fitness.log_meal(
|
||||
description=structured.get("description") or arguments.get("text", ""),
|
||||
meal_type=arguments.get("meal_type") or structured.get("meal_type") or "snack",
|
||||
calories=float(structured.get("calories") or 0),
|
||||
protein_g=float(structured.get("protein_g") or 0),
|
||||
fat_g=float(structured.get("fat_g") or 0),
|
||||
carbs_g=float(structured.get("carbs_g") or 0),
|
||||
source="llm",
|
||||
estimated=True,
|
||||
)
|
||||
elif name == "log_water":
|
||||
result = fitness.log_water(int(arguments.get("amount_ml", 250)))
|
||||
elif name == "log_weight":
|
||||
result = fitness.log_weight(
|
||||
float(arguments["weight_kg"]),
|
||||
body_fat_pct=arguments.get("body_fat_pct"),
|
||||
notes=arguments.get("notes", ""),
|
||||
)
|
||||
elif name == "log_workout":
|
||||
structured = await structure_workout(arguments.get("text", ""))
|
||||
result = fitness.log_workout(
|
||||
title=structured.get("title") or "Тренировка",
|
||||
notes=structured.get("notes") or arguments.get("text", ""),
|
||||
duration_min=structured.get("duration_min"),
|
||||
exercises=structured.get("exercises"),
|
||||
)
|
||||
elif name == "lookup_food":
|
||||
result = OpenFoodFactsClient().search(
|
||||
arguments.get("query", ""),
|
||||
limit=arguments.get("limit", 5),
|
||||
)
|
||||
elif name == "lookup_exercise":
|
||||
result = WgerClient().search_exercises(
|
||||
arguments.get("query", ""),
|
||||
limit=arguments.get("limit", 8),
|
||||
)
|
||||
elif name == "set_fitness_reminder":
|
||||
result = fitness.set_reminder(
|
||||
arguments.get("kind", "water"),
|
||||
enabled=arguments.get("enabled"),
|
||||
hour=arguments.get("hour"),
|
||||
minute=arguments.get("minute"),
|
||||
interval_hours=arguments.get("interval_hours"),
|
||||
)
|
||||
else:
|
||||
return json.dumps({"error": f"Unknown tool: {name}"}, ensure_ascii=False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user