Files
sport-science-coach/js/openai-service.js
T
2025-04-27 14:31:03 +02:00

233 lines
10 KiB
JavaScript

/**
* OpenAI Service Module
* Handles interactions with the OpenAI API for generating health recommendations
*/
class OpenAIService {
constructor() {
this.apiKey = null;
this.model = CONFIG.openai.model;
this.temperature = CONFIG.openai.temperature;
this.maxTokens = CONFIG.openai.max_tokens;
}
/**
* Set the API key for OpenAI requests
* @param {string} apiKey - OpenAI API key
*/
setApiKey(apiKey) {
this.apiKey = apiKey;
}
/**
* Check if API key is set
* @returns {boolean} - Whether API key is set
*/
hasApiKey() {
return !!this.apiKey;
}
/**
* Generate a system prompt based on user configuration
* @param {Object} options - Analysis options
* @returns {string} - Complete system prompt
*/
generateSystemPrompt(options) {
// Start with the base prompt
let systemPrompt = CONFIG.systemPrompts.base;
// Add analysis focus prompt
if (options.analysisFocus && CONFIG.systemPrompts[options.analysisFocus]) {
systemPrompt += '\n\n' + CONFIG.systemPrompts[options.analysisFocus];
}
// Add recommendation style prompt
if (options.recommendationStyle &&
CONFIG.recommendationStyles[options.recommendationStyle]) {
systemPrompt += '\n\n' + CONFIG.recommendationStyles[options.recommendationStyle].prompt;
}
// Add report detail level prompt
if (options.reportDetail &&
CONFIG.reportDetailLevels[options.reportDetail]) {
systemPrompt += '\n\n' + CONFIG.reportDetailLevels[options.reportDetail].prompt;
}
return systemPrompt;
}
/**
* Generate a user prompt with data and custom instructions
* @param {Object} data - Processed health and workout data
* @param {string} customPrompt - User's custom instructions
* @returns {string} - Complete user prompt
*/
generateUserPrompt(data, customPrompt) {
// Create a structured representation of the data
const dataString = JSON.stringify(data, null, 2);
let userPrompt = `Please analyze the following health metrics and workout data and provide personalized recommendations:
DATA:
${dataString}`;
// Add custom instructions if provided
if (customPrompt && customPrompt.trim()) {
userPrompt += `\n\nADDITIONAL INSTRUCTIONS:
${customPrompt}`;
}
userPrompt += `\n\nPlease format your response in HTML for direct display in a web application. Use h2, h3, p, ul, li, and other appropriate HTML tags to structure your response. You may use <strong>, <em>, and other formatting tags as needed. Do not include <!DOCTYPE>, <html>, <head>, or <body> tags.
Note: Data visualizations will be automatically generated for sleep patterns, stress levels, workout performance, and recovery metrics, so you don't need to describe these visually in your response. Focus on providing insights and recommendations based on the data.`;
return userPrompt;
}
/**
* Generate health recommendations using OpenAI API
* @param {Object} data - Processed health and workout data
* @param {Object} options - Analysis options
* @returns {Promise} - Promise resolving with the generated recommendations and raw input data
*/
async generateRecommendations(data, options) {
if (!this.hasApiKey()) {
throw new Error('OpenAI API key not set. Please set an API key before generating recommendations.');
}
try {
const systemPrompt = this.generateSystemPrompt(options);
const userPrompt = this.generateUserPrompt(data, options.customPrompt);
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
model: this.model,
messages: [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: userPrompt
}
],
temperature: this.temperature,
max_tokens: this.maxTokens
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`OpenAI API Error: ${errorData.error?.message || 'Unknown error'}`);
}
const result = await response.json();
return {
content: result.choices[0].message.content,
rawInput: {
systemPrompt,
userPrompt
}
};
} catch (error) {
console.error('Error generating recommendations:', error);
throw error;
}
}
/**
* Generate a demo response without using the API
* @param {Object} data - Processed health and workout data
* @param {Object} options - Analysis options
* @returns {Promise} - Promise resolving with the generated demo recommendations and raw input data
*/
async generateDemoRecommendations(data, options) {
// This function provides a demo response when no API key is available
// It returns a pre-written HTML response based on the data and options
// Generate the prompts that would be sent to the API
const systemPrompt = this.generateSystemPrompt(options);
const userPrompt = this.generateUserPrompt(data, options.customPrompt);
// Extract some basic stats for the demo
const sleepAvg = data.summary.sleep?.average.toFixed(1) || 'N/A';
const workoutCount = data.summary.workouts?.count || 0;
const stressLevels = Object.keys(data.summary.stress?.qualifiers || {}).join(', ');
const dateRange = `${data.summary.dateRange?.start || 'N/A'} to ${data.summary.dateRange?.end || 'N/A'}`;
// Create a demo HTML response
const demoHTML = `
<div class="report-section">
<h2>Health & Fitness Analysis</h2>
<p class="report-date">Analysis Period: ${dateRange}</p>
<div class="report-summary">
<h3>Summary</h3>
<p>This analysis is based on ${data.metrics.length} days of health metrics and ${workoutCount} workouts. The data shows an average sleep duration of ${sleepAvg} hours per night with stress levels categorized as: ${stressLevels}.</p>
<p><strong>Note:</strong> This is a demo analysis. For a complete analysis, please provide an OpenAI API key.</p>
</div>
<div class="report-section">
<h3>Sleep Analysis</h3>
<p>Your average sleep duration of ${sleepAvg} hours is ${sleepAvg >= 7 ? 'within the recommended range' : 'below the recommended range'} for optimal health. Sleep quality indicators suggest ${sleepAvg >= 7 ? 'adequate' : 'potential issues with'} recovery between activities.</p>
<ul>
<li><strong>Recommendation:</strong> ${sleepAvg >= 7 ? 'Maintain your current sleep schedule' : 'Aim to increase sleep duration by 30-60 minutes'} to optimize recovery and performance.</li>
<li><strong>Recommendation:</strong> Consider implementing a consistent pre-sleep routine to improve sleep quality.</li>
</ul>
</div>
<div class="report-section">
<h3>Stress Management</h3>
<p>Your stress levels show ${stressLevels.includes('balanced') ? 'a good balance overall' : 'potential areas for improvement'}, with some periods of elevated stress detected.</p>
<ul>
<li><strong>Recommendation:</strong> Incorporate daily mindfulness practices (5-10 minutes) to manage stress levels.</li>
<li><strong>Recommendation:</strong> Consider scheduling regular recovery days, especially following high-stress periods.</li>
</ul>
</div>
<div class="report-section">
<h3>Workout Analysis</h3>
<p>Your training data shows ${workoutCount} workouts during the analysis period, with consistent effort levels.</p>
<ul>
<li><strong>Recommendation:</strong> ${workoutCount >= 3 ? 'Maintain your current workout frequency' : 'Consider increasing workout frequency to 3-4 sessions per week'} for optimal fitness development.</li>
<li><strong>Recommendation:</strong> Incorporate varied intensity levels in your training to stimulate different energy systems.</li>
</ul>
</div>
<div class="report-section">
<h3>Next Steps</h3>
<p>Based on this analysis, focus on the following areas:</p>
<ol>
<li>${sleepAvg >= 7 ? 'Maintain your sleep consistency' : 'Improve sleep duration and quality'}</li>
<li>Implement regular stress management practices</li>
<li>${workoutCount >= 3 ? 'Continue your consistent training schedule' : 'Increase workout frequency gradually'}</li>
<li>Monitor your progress and adjust based on recovery indicators</li>
</ol>
</div>
</div>
`;
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1500));
return {
content: demoHTML,
rawInput: {
systemPrompt,
userPrompt
}
};
}
}
// Create a global instance
const openaiService = new OpenAIService();