Files
gymflow/services/geminiService.ts
2025-11-28 18:20:22 +02:00

127 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { WorkoutSession, UserProfile, WorkoutPlan } from '../types';
import { api } from './api';
import { generateId } from '../utils/uuid';
interface FitnessChatOptions {
history: WorkoutSession[];
userProfile?: UserProfile;
plans?: WorkoutPlan[];
lang?: 'en' | 'ru';
sessionId?: string;
}
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 = generateId();
// 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,
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 Coach.
Твоя задача — анализировать тренировки пользователя и давать краткие, полезные советы на русском языке.
ДАННЫЕ ПОЛЬЗОВАТЕЛЯ:
${userProfile ? `
- Вес: ${userProfile.weight || 'не указан'} кг
- Рост: ${userProfile.height || 'не указан'} см
- Пол: ${userProfile.gender || 'не указан'}
` : 'Профиль не заполнен'}
ТРЕНИРОВОЧНЫЕ ПЛАНЫ:
${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 called AI Coach.
Your task is to analyze the user's workouts and provide concise, helpful advice in English.
USER PROFILE:
${userProfile ? `
- Weight: ${userProfile.weight || 'not specified'} kg
- Height: ${userProfile.height || 'not specified'} cm
- Gender: ${userProfile.gender || 'not specified'}
` : 'Profile not filled'}
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,
sessionId
});
return {
text: res.response,
response: {
text: () => res.response
}
};
}
};
};