first commit
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
using Fitness_core_BE.Helpers;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Fitness_core_BE.AI
|
||||
{
|
||||
public class WorkoutPlanOrchestrator
|
||||
{
|
||||
private readonly IAiClient _aiClient;
|
||||
private readonly ModificationGuard _guard;
|
||||
|
||||
public WorkoutPlanOrchestrator(IAiClient aiClient, ModificationGuard guard)
|
||||
{
|
||||
_aiClient = aiClient;
|
||||
_guard = guard;
|
||||
}
|
||||
|
||||
public async Task<object> GeneratePlanAsync(object payload)
|
||||
{
|
||||
var data = DeserializePayload.Deserialize(payload);
|
||||
|
||||
string fitnessLevel = DataExtractor.GetRequiredString(data, "fitnessLevel");
|
||||
string goal = DataExtractor.GetRequiredString(data, "goal");
|
||||
int daysPerWeek = DataExtractor.GetRequiredInt(data, "daysPerWeek");
|
||||
string availableEquipment = DataExtractor.GetRequiredString(data, "availableEquipment");
|
||||
int durationMinutes = DataExtractor.GetRequiredInt(data, "durationPerSession");
|
||||
|
||||
// Optional fields
|
||||
string? injuryOrLimitations = data.ContainsKey("injuryOrLimitations")
|
||||
? data["injuryOrLimitations"].GetString()
|
||||
: null;
|
||||
|
||||
// Basic validation
|
||||
if (daysPerWeek < 1 || daysPerWeek > 7)
|
||||
throw new Exception("Days per week must be between 1 and 7");
|
||||
|
||||
if (durationMinutes < 15 || durationMinutes > 180)
|
||||
throw new Exception("Duration must be between 15 and 180 minutes");
|
||||
|
||||
|
||||
//bmi, body shape , target , should check with tartget and body shape matching, time peiod, Continuose-week days,,, whether going to Gym or not, Injuries, any seriose conditions,
|
||||
|
||||
var systemPrompt = @"
|
||||
You are a certified personal trainer and fitness expert.
|
||||
Your response must be STRICTLY a JSON object.
|
||||
|
||||
Output format rules:
|
||||
1. The first character of your response MUST be {
|
||||
2. The last character MUST be }
|
||||
3. Do not include ``` or any markdown formatting
|
||||
4. Do not include any explanation text
|
||||
5. Do not include comments
|
||||
6. Do not prefix with ""Here is""
|
||||
7. Do not suffix with any text
|
||||
|
||||
Return ONLY valid JSON.
|
||||
|
||||
Structure:
|
||||
{
|
||||
""name"": string,
|
||||
""description"": string,
|
||||
""workoutItems"": [
|
||||
{
|
||||
""exerciseDay"": string,
|
||||
""description"": string,
|
||||
""exercises"": {
|
||||
""Exercise Name"": ""sets x reps or duration""
|
||||
},
|
||||
""duration"": string (total session time including warmup and cooldown, with units),
|
||||
""wormUp"": string (warmup instructions with an explicit time and unit),
|
||||
""cooldown"": string (cooldown instructions with an explicit time and unit),
|
||||
""howToDo"": string (JSON string with rest periods and notes),
|
||||
""doURL"": string (exercise reference URL)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Important guidelines:
|
||||
- Create a workout plan based on the user's fitness level, goal, and available equipment
|
||||
- Include appropriate exercises for the specified number of days per week
|
||||
- Each workout must fit within the specified duration
|
||||
- Set ""duration"" to the requested total duration per session (for example, ""45 minutes"")
|
||||
- ""wormUp"" and ""cooldown"" must each include an explicit duration such as ""5 minutes""
|
||||
- The main workout time plus wormUp time plus cooldown time must equal the total ""duration""
|
||||
- The ""exercises"" field should be an object with exercise names as keys and sets/reps as values
|
||||
- The ""howToDo"" field should be a JSON string containing warmup, rest periods, and notes
|
||||
- Consider any injury or limitations mentioned
|
||||
- Provide realistic sets, reps, and rest periods based on fitness level
|
||||
- Include proper progression and variety
|
||||
";
|
||||
|
||||
var jsonResponse = await _aiClient.GenerateJsonAsync(
|
||||
systemPrompt,
|
||||
data
|
||||
);
|
||||
|
||||
// Validate JSON before returning
|
||||
try
|
||||
{
|
||||
var parsed = JsonSerializer.Deserialize<object>(jsonResponse);
|
||||
if (parsed == null)
|
||||
throw new Exception("Invalid AI JSON");
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception("AI returned invalid JSON structure");
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize<object>(jsonResponse)!;
|
||||
}
|
||||
|
||||
public async Task<object> UpdatePlanAsync(object payload)
|
||||
{
|
||||
var data = DeserializePayload.Deserialize(payload);
|
||||
|
||||
if (!data.ContainsKey("currentWorkoutPlan"))
|
||||
throw new Exception("Current plan missing");
|
||||
|
||||
string modification = DataExtractor.GetRequiredString(data, "modification");
|
||||
|
||||
_guard.Validate(modification);
|
||||
|
||||
var systemPrompt = @"
|
||||
You are a professional fitness AI.
|
||||
|
||||
You will receive:
|
||||
1) The current workout plan in valid JSON format.
|
||||
2) A user modification request in free text.
|
||||
3) BMI, body shape, target, time period, continuous workout days, whether going to gym or not, injuries, any seriose conditions.
|
||||
|
||||
Your task:
|
||||
- Carefully analyze the modification request.
|
||||
- Modify ONLY the specific elements explicitly requested by the user.
|
||||
- Do NOT change anything else.
|
||||
|
||||
VERY IMPORTANT RULES:
|
||||
- Do NOT change the plan name unless the user explicitly asks.
|
||||
- Do NOT change the number of workout days unless requested.
|
||||
- Do NOT modify exercises that are not mentioned in the request.
|
||||
- Do NOT rewrite descriptions unless necessary for the modification.
|
||||
- Do NOT change sets, reps, or rest periods unless the request requires it.
|
||||
- Do NOT add or remove exercises unless explicitly requested.
|
||||
- Do NOT modify workout structure that is unrelated to the request.
|
||||
- Preserve every unchanged value exactly as provided.
|
||||
- Analyze the user's BMI, body shape, target, time period, continuous workout days, gym access, injuries, and any seriose conditions.
|
||||
- Stricly focus if mentioned any seriose health conditions or injuries and do not suggest any exercise that can cause harm to the user.
|
||||
- If the modification request is not logical or contradicts fitness principles, do not apply it and explain why in the reason field.
|
||||
|
||||
|
||||
Scope control:
|
||||
If the request only affects one exercise, update only that exercise.
|
||||
If the request does not affect the plan, return it unchanged.
|
||||
|
||||
Preserve the exact internal structure of the workout plan.
|
||||
Do NOT rename fields.
|
||||
Do NOT remove required properties.
|
||||
Do NOT change the JSON schema.
|
||||
|
||||
Return your response strictly in this JSON format:
|
||||
|
||||
{
|
||||
""reason"": string,
|
||||
""modified"": boolean,
|
||||
""currentPlan"": {
|
||||
""name"": string,
|
||||
""description"": string,
|
||||
""workoutItems"": [
|
||||
{
|
||||
""exerciseDay"": string,
|
||||
""description"": string,
|
||||
""exercises"": {
|
||||
""Exercise Name"": ""sets x reps or duration""
|
||||
},
|
||||
""howToDo"": string,
|
||||
""doURL"": string
|
||||
}
|
||||
]
|
||||
},
|
||||
""updatedPlan"": {
|
||||
""name"": string,
|
||||
""description"": string,
|
||||
""workoutItems"": [
|
||||
{
|
||||
""exerciseDay"": string,
|
||||
""description"": string,
|
||||
""exercises"": {
|
||||
""Exercise Name"": ""sets x reps or duration""
|
||||
},
|
||||
""howToDo"": string,
|
||||
""doURL"": string
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
STRICT OUTPUT RULES:
|
||||
- Return ONLY valid raw JSON.
|
||||
- No markdown.
|
||||
- No explanations.
|
||||
- First character must be {.
|
||||
- Last character must be }.
|
||||
- No trailing commas.
|
||||
- No extra commentary.
|
||||
|
||||
If modification cannot be applied:
|
||||
- Set ""modified"": false
|
||||
- Keep currentPlan and updatedPlan identical.
|
||||
- Explain reason clearly.
|
||||
";
|
||||
|
||||
var jsonResponse = await _aiClient.GenerateJsonAsync(
|
||||
systemPrompt,
|
||||
data
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
var parsed = JsonSerializer.Deserialize<object>(jsonResponse);
|
||||
if (parsed == null)
|
||||
throw new Exception("Invalid JSON");
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception("AI returned invalid JSON");
|
||||
}
|
||||
|
||||
return JsonSerializer.Deserialize<object>(jsonResponse)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user