AI coach fixed: acquired access to context data
This commit is contained in:
@@ -1,49 +1,119 @@
|
||||
import { WorkoutSession } from '../types';
|
||||
import { WorkoutSession, UserProfile, WorkoutPlan } from '../types';
|
||||
import { api } from './api';
|
||||
|
||||
export const createFitnessChat = (history: WorkoutSession[], lang: 'en' | 'ru' = 'ru'): any => {
|
||||
// The original returned a Chat object.
|
||||
// Now we need to return something that behaves like it or refactor the UI.
|
||||
// The UI likely calls `chat.sendMessage(msg)`.
|
||||
// So we return an object with `sendMessage`.
|
||||
interface FitnessChatOptions {
|
||||
history: WorkoutSession[];
|
||||
userProfile?: UserProfile;
|
||||
plans?: WorkoutPlan[];
|
||||
lang?: 'en' | 'ru';
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
// Summarize data to reduce token count while keeping relevant context
|
||||
const summary = history.slice(0, 10).map(s => ({
|
||||
export const createFitnessChat = (
|
||||
history: WorkoutSession[],
|
||||
lang: 'en' | 'ru' = 'ru',
|
||||
userProfile?: UserProfile,
|
||||
plans?: WorkoutPlan[]
|
||||
): any => {
|
||||
// Generate a unique session ID for this chat instance
|
||||
const sessionId = crypto.randomUUID();
|
||||
|
||||
// Summarize workout history
|
||||
const workoutSummary = history.slice(0, 20).map(s => ({
|
||||
date: new Date(s.startTime).toLocaleDateString(lang === 'ru' ? 'ru-RU' : 'en-US'),
|
||||
userWeight: s.userBodyWeight,
|
||||
exercises: s.sets.map(set => `${set.exerciseName}: ${set.weight ? set.weight + (lang === 'ru' ? 'кг' : 'kg') : ''}${set.reps ? ' x ' + set.reps + (lang === 'ru' ? 'повт' : 'reps') : ''} ${set.distanceMeters ? set.distanceMeters + (lang === 'ru' ? 'м' : 'm') : ''}`).join(', ')
|
||||
planName: s.planName,
|
||||
exercises: s.sets.map(set => {
|
||||
const parts = [];
|
||||
parts.push(set.exerciseName);
|
||||
if (set.weight) parts.push(`${set.weight}${lang === 'ru' ? 'кг' : 'kg'}`);
|
||||
if (set.reps) parts.push(`${set.reps}${lang === 'ru' ? 'повт' : 'reps'}`);
|
||||
if (set.distanceMeters) parts.push(`${set.distanceMeters}${lang === 'ru' ? 'м' : 'm'}`);
|
||||
if (set.durationSeconds) parts.push(`${set.durationSeconds}${lang === 'ru' ? 'сек' : 's'}`);
|
||||
return parts.join(' ');
|
||||
}).join(', ')
|
||||
}));
|
||||
|
||||
// Calculate personal records
|
||||
const exerciseRecords = new Map<string, { maxWeight?: number; maxReps?: number; maxDistance?: number }>();
|
||||
history.forEach(session => {
|
||||
session.sets.forEach(set => {
|
||||
const current = exerciseRecords.get(set.exerciseName) || {};
|
||||
exerciseRecords.set(set.exerciseName, {
|
||||
maxWeight: Math.max(current.maxWeight || 0, set.weight || 0),
|
||||
maxReps: Math.max(current.maxReps || 0, set.reps || 0),
|
||||
maxDistance: Math.max(current.maxDistance || 0, set.distanceMeters || 0)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const personalRecords = Array.from(exerciseRecords.entries()).map(([name, records]) => ({
|
||||
exercise: name,
|
||||
...records
|
||||
}));
|
||||
|
||||
// Build comprehensive system instruction
|
||||
const systemInstruction = lang === 'ru' ? `
|
||||
Ты — опытный и поддерживающий фитнес-тренер.
|
||||
Ты — опытный и поддерживающий фитнес-тренер AI Expert.
|
||||
Твоя задача — анализировать тренировки пользователя и давать краткие, полезные советы на русском языке.
|
||||
|
||||
Учитывай вес пользователя (userWeight в json), если он указан, при анализе прогресса в упражнениях с собственным весом.
|
||||
ДАННЫЕ ПОЛЬЗОВАТЕЛЯ:
|
||||
${userProfile ? `
|
||||
- Вес: ${userProfile.weight || 'не указан'} кг
|
||||
- Рост: ${userProfile.height || 'не указан'} см
|
||||
- Пол: ${userProfile.gender || 'не указан'}
|
||||
` : 'Профиль не заполнен'}
|
||||
|
||||
Вот последние 10 тренировок пользователя (в формате JSON):
|
||||
${JSON.stringify(summary)}
|
||||
|
||||
Если пользователь спрашивает о прогрессе, используй эти данные.
|
||||
Отвечай емко, мотивирующе. Избегай длинных лекций, если не просили.
|
||||
ТРЕНИРОВОЧНЫЕ ПЛАНЫ:
|
||||
${plans && plans.length > 0 ? JSON.stringify(plans.map(p => ({ name: p.name, exercises: p.steps.map(s => s.exerciseName) }))) : 'Нет активных планов'}
|
||||
|
||||
ИСТОРИЯ ТРЕНИРОВОК (последние 20):
|
||||
${JSON.stringify(workoutSummary, null, 2)}
|
||||
|
||||
ЛИЧНЫЕ РЕКОРДЫ:
|
||||
${JSON.stringify(personalRecords, null, 2)}
|
||||
|
||||
ИНСТРУКЦИИ:
|
||||
- Используй эти данные для анализа прогресса и ответов на вопросы
|
||||
- Учитывай вес пользователя при анализе упражнений с собственным весом
|
||||
- Будь конкретным и ссылайся на реальные данные из истории
|
||||
- Отвечай емко, мотивирующе
|
||||
- Если данных недостаточно для ответа, честно скажи об этом
|
||||
` : `
|
||||
You are an experienced and supportive fitness coach.
|
||||
You are an experienced and supportive fitness coach called AI Expert.
|
||||
Your task is to analyze the user's workouts and provide concise, helpful advice in English.
|
||||
|
||||
Consider the user's weight (userWeight in json), if provided, when analyzing progress in bodyweight exercises.
|
||||
USER PROFILE:
|
||||
${userProfile ? `
|
||||
- Weight: ${userProfile.weight || 'not specified'} kg
|
||||
- Height: ${userProfile.height || 'not specified'} cm
|
||||
- Gender: ${userProfile.gender || 'not specified'}
|
||||
` : 'Profile not filled'}
|
||||
|
||||
Here are the user's last 10 workouts (in JSON format):
|
||||
${JSON.stringify(summary)}
|
||||
|
||||
If the user asks about progress, use this data.
|
||||
Answer concisely and motivationally. Avoid long lectures unless asked.
|
||||
ALWAYS answer in the language the user speaks to you, defaulting to English if unsure.
|
||||
WORKOUT PLANS:
|
||||
${plans && plans.length > 0 ? JSON.stringify(plans.map(p => ({ name: p.name, exercises: p.steps.map(s => s.exerciseName) }))) : 'No active plans'}
|
||||
|
||||
WORKOUT HISTORY (last 20 sessions):
|
||||
${JSON.stringify(workoutSummary, null, 2)}
|
||||
|
||||
PERSONAL RECORDS:
|
||||
${JSON.stringify(personalRecords, null, 2)}
|
||||
|
||||
INSTRUCTIONS:
|
||||
- Use this data to analyze progress and answer questions
|
||||
- Consider user's weight when analyzing bodyweight exercises
|
||||
- Be specific and reference actual data from the history
|
||||
- Answer concisely and motivationally
|
||||
- If there's insufficient data to answer, be honest about it
|
||||
- ALWAYS answer in the language the user speaks to you
|
||||
`;
|
||||
|
||||
return {
|
||||
sendMessage: async (userMessage: string) => {
|
||||
const res = await api.post('/ai/chat', {
|
||||
systemInstruction,
|
||||
userMessage
|
||||
userMessage,
|
||||
sessionId
|
||||
});
|
||||
return {
|
||||
text: res.response,
|
||||
|
||||
Reference in New Issue
Block a user