mirror of
https://github.com/sstent/AICycling_mcp.git
synced 2025-12-06 08:01:57 +00:00
working
This commit is contained in:
@@ -6,6 +6,8 @@ CLI Interface - Simple command line interface for the cycling analyzer
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
from config import Config, load_config, create_sample_config
|
from config import Config, load_config, create_sample_config
|
||||||
from core_app import CyclingAnalyzerApp
|
from core_app import CyclingAnalyzerApp
|
||||||
@@ -17,11 +19,8 @@ class CLI:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = None
|
self.app = None
|
||||||
|
|
||||||
async def run(self):
|
async def run(self, args):
|
||||||
"""Main CLI loop"""
|
"""Main CLI loop"""
|
||||||
print("Cycling Workout Analyzer")
|
|
||||||
print("=" * 40)
|
|
||||||
|
|
||||||
# Setup configuration
|
# Setup configuration
|
||||||
try:
|
try:
|
||||||
config = self._setup_config()
|
config = self._setup_config()
|
||||||
@@ -33,11 +32,13 @@ class CLI:
|
|||||||
# Initialize app
|
# Initialize app
|
||||||
await self.app.initialize()
|
await self.app.initialize()
|
||||||
|
|
||||||
# Show initial status
|
if args.command:
|
||||||
self._show_status()
|
await self._handle_command(args)
|
||||||
|
else:
|
||||||
# Main loop
|
# Show initial status
|
||||||
await self._main_loop()
|
await self._show_status()
|
||||||
|
# Main loop
|
||||||
|
await self._main_loop()
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nGoodbye!")
|
print("\nGoodbye!")
|
||||||
@@ -67,10 +68,10 @@ class CLI:
|
|||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def _show_status(self):
|
async def _show_status(self):
|
||||||
"""Show application status"""
|
"""Show application status"""
|
||||||
print(f"\nStatus:")
|
print(f"\nStatus:")
|
||||||
print(f"- Available tools: {len(self.app.list_available_tools())}")
|
print(f"- Available tools: {len(await self.app.list_available_tools())}")
|
||||||
print(f"- Available templates: {len(self.app.list_templates())}")
|
print(f"- Available templates: {len(self.app.list_templates())}")
|
||||||
print(f"- Cached data keys: {list(self.app.get_cached_data().keys())}")
|
print(f"- Cached data keys: {list(self.app.get_cached_data().keys())}")
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ class CLI:
|
|||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
await self._enhanced_analysis()
|
await self._enhanced_analysis()
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
self._list_tools()
|
await self._list_tools()
|
||||||
elif choice == "5":
|
elif choice == "5":
|
||||||
self._list_templates()
|
self._list_templates()
|
||||||
elif choice == "6":
|
elif choice == "6":
|
||||||
@@ -184,9 +185,9 @@ class CLI:
|
|||||||
print("="*50)
|
print("="*50)
|
||||||
print(result)
|
print(result)
|
||||||
|
|
||||||
def _list_tools(self):
|
async def _list_tools(self):
|
||||||
"""List available tools"""
|
"""List available tools"""
|
||||||
tools = self.app.list_available_tools()
|
tools = await self.app.list_available_tools()
|
||||||
if tools:
|
if tools:
|
||||||
self.app.mcp_client.print_tools()
|
self.app.mcp_client.print_tools()
|
||||||
else:
|
else:
|
||||||
@@ -247,10 +248,21 @@ Weekly Structure:
|
|||||||
f.write(default_rules)
|
f.write(default_rules)
|
||||||
return default_rules
|
return default_rules
|
||||||
|
|
||||||
|
async def _handle_command(self, args):
|
||||||
|
"""Handle non-interactive commands"""
|
||||||
|
if args.command == "analyze_last":
|
||||||
|
await self._analyze_last_workout()
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {args.command}")
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
"""CLI entry point"""
|
"""CLI entry point"""
|
||||||
|
parser = argparse.ArgumentParser(description="Cycling Workout Analyzer")
|
||||||
|
parser.add_argument('command', nargs='?', help="Command to execute (e.g., analyze_last)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
cli = CLI()
|
cli = CLI()
|
||||||
await cli.run()
|
await cli.run(args)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
20
core_app.py
20
core_app.py
@@ -70,7 +70,7 @@ class CyclingAnalyzerApp:
|
|||||||
act for act in activities
|
act for act in activities
|
||||||
if "cycling" in act.get("activityType", {}).get("typeKey", "").lower()
|
if "cycling" in act.get("activityType", {}).get("typeKey", "").lower()
|
||||||
]
|
]
|
||||||
return max(cycling_activities, key=lambda x: x.get("start_time", "")) if cycling_activities else None
|
return max(cycling_activities, key=lambda x: x.get("startTimeGmt", 0)) if cycling_activities else None
|
||||||
|
|
||||||
# Core functionality methods
|
# Core functionality methods
|
||||||
|
|
||||||
@@ -80,13 +80,13 @@ class CyclingAnalyzerApp:
|
|||||||
|
|
||||||
# Prepare context data
|
# Prepare context data
|
||||||
context = {
|
context = {
|
||||||
"user_profile": self.cache_manager.get("user_profile", {}),
|
"user_info": self.cache_manager.get("user_profile", {}),
|
||||||
"recent_activities": self.cache_manager.get("recent_activities", []),
|
"activity_summary": self.cache_manager.get("last_cycling_details", {}),
|
||||||
"last_cycling_details": self.cache_manager.get("last_cycling_details", {}),
|
|
||||||
**kwargs
|
**kwargs
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load and render template
|
# Load and render template
|
||||||
|
logger.info(f"Rendering template {template_name} with context keys: {list(context.keys())}")
|
||||||
prompt = self.template_engine.render(template_name, **context)
|
prompt = self.template_engine.render(template_name, **context)
|
||||||
|
|
||||||
# Call LLM
|
# Call LLM
|
||||||
@@ -122,9 +122,9 @@ class CyclingAnalyzerApp:
|
|||||||
|
|
||||||
# Utility methods
|
# Utility methods
|
||||||
|
|
||||||
def list_available_tools(self) -> list:
|
async def list_available_tools(self) -> list:
|
||||||
"""Get list of available MCP tools"""
|
"""Get list of available MCP tools"""
|
||||||
return self.mcp_client.list_tools()
|
return await self.mcp_client.list_tools()
|
||||||
|
|
||||||
def list_templates(self) -> list:
|
def list_templates(self) -> list:
|
||||||
"""Get list of available templates"""
|
"""Get list of available templates"""
|
||||||
@@ -145,13 +145,13 @@ async def main():
|
|||||||
await app.initialize()
|
await app.initialize()
|
||||||
|
|
||||||
# Example usage
|
# Example usage
|
||||||
print("Available tools:", len(app.list_available_tools()))
|
print("Available tools:", len(await app.list_available_tools()))
|
||||||
print("Available templates:", len(app.list_templates()))
|
print("Available templates:", len(app.list_templates()))
|
||||||
|
|
||||||
# Run analysis
|
# Run analysis
|
||||||
analysis = await app.analyze_workout("analyze_last_workout",
|
# analysis = await app.analyze_workout("analyze_last_workout",
|
||||||
training_rules="Sample rules")
|
# training_rules="Sample rules")
|
||||||
print("Analysis:", analysis[:200] + "...")
|
# print("Analysis:", analysis[:200] + "...")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Application error: {e}")
|
logger.error(f"Application error: {e}")
|
||||||
|
|||||||
17
rules.yaml
Normal file
17
rules.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
Training Goals:
|
||||||
|
- Improve FTP (Functional Threshold Power)
|
||||||
|
- Build endurance for long rides
|
||||||
|
- Maintain consistent training
|
||||||
|
|
||||||
|
Power Zones (adjust based on your FTP):
|
||||||
|
- Zone 1 (Active Recovery): < 55% FTP
|
||||||
|
- Zone 2 (Endurance): 56-75% FTP
|
||||||
|
- Zone 3 (Tempo): 76-90% FTP
|
||||||
|
- Zone 4 (Lactate Threshold): 91-105% FTP
|
||||||
|
- Zone 5 (VO2 Max): 106-120% FTP
|
||||||
|
|
||||||
|
Weekly Structure:
|
||||||
|
- 70-80% easy/moderate intensity
|
||||||
|
- 20-30% high intensity
|
||||||
|
- At least 1 rest day per week
|
||||||
@@ -122,11 +122,13 @@ class TemplateEngine:
|
|||||||
try:
|
try:
|
||||||
section_content = self.load_template(section_file)
|
section_content = self.load_template(section_file)
|
||||||
# Render section with same kwargs
|
# Render section with same kwargs
|
||||||
section_rendered = section_content.format(**kwargs)
|
# Recursively render the section content
|
||||||
|
section_rendered = self.render(section_file, **kwargs)
|
||||||
content = content.replace(placeholder, section_rendered)
|
content = content.replace(placeholder, section_rendered)
|
||||||
except (FileNotFoundError, KeyError) as e:
|
except (FileNotFoundError, KeyError, ValueError) as e:
|
||||||
logger.warning(f"Could not process section {section_name}: {e}")
|
logger.warning(f"Could not process section {section_name}: {e}")
|
||||||
# Leave placeholder as-is if section can't be loaded
|
# Replace with empty string if section fails
|
||||||
|
content = content.replace(placeholder, "")
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|||||||
7
templates/base/analysis_frameworks/assessment_points.txt
Normal file
7
templates/base/analysis_frameworks/assessment_points.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Please provide:
|
||||||
|
1. Overall assessment of the workout
|
||||||
|
2. How well it aligns with my rules and goals
|
||||||
|
3. Areas for improvement
|
||||||
|
4. Specific feedback on power, heart rate, duration, and intensity
|
||||||
|
5. Recovery recommendations
|
||||||
|
6. Comparison with typical performance metrics
|
||||||
2
templates/base/data_sections/activity_summary.txt
Normal file
2
templates/base/data_sections/activity_summary.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ACTIVITY SUMMARY:
|
||||||
|
Activity ID: {activity_summary[activityId]}
|
||||||
2
templates/base/data_sections/training_rules.txt
Normal file
2
templates/base/data_sections/training_rules.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
My training rules and goals:
|
||||||
|
{training_rules}
|
||||||
2
templates/base/data_sections/user_info.txt
Normal file
2
templates/base/data_sections/user_info.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
USER INFO:
|
||||||
|
User ID: {user_info[id]}
|
||||||
3
templates/base/system_prompts/main_agent.txt
Normal file
3
templates/base/system_prompts/main_agent.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
You are an expert cycling coach with access to comprehensive Garmin Connect data.
|
||||||
|
You analyze cycling workouts, provide performance insights, and give actionable training recommendations.
|
||||||
|
Use the available tools to gather detailed workout data and provide comprehensive analysis.
|
||||||
2
templates/base/system_prompts/no_tools_analysis.txt
Normal file
2
templates/base/system_prompts/no_tools_analysis.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
You are an expert cycling coach. Perform comprehensive analysis using the provided data.
|
||||||
|
Do not use any tools - all relevant data is included in the prompt.
|
||||||
11
templates/workflows/analyze_last_workout.txt
Normal file
11
templates/workflows/analyze_last_workout.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
Analyze my most recent cycling workout using the provided data.
|
||||||
|
|
||||||
|
{activity_summary_section}
|
||||||
|
|
||||||
|
{user_info_section}
|
||||||
|
|
||||||
|
{training_rules_section}
|
||||||
|
|
||||||
|
{assessment_points}
|
||||||
|
|
||||||
|
Focus on the provided activity details for your analysis.
|
||||||
5
templates/workflows/enhanced_analysis.txt
Normal file
5
templates/workflows/enhanced_analysis.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Perform enhanced {analysis_type} analysis using all available data and tools.
|
||||||
|
|
||||||
|
Available cached data: {cached_data}
|
||||||
|
|
||||||
|
Use MCP tools as needed to gather additional data for comprehensive analysis.
|
||||||
10
templates/workflows/suggest_next_workout.txt
Normal file
10
templates/workflows/suggest_next_workout.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Please suggest my next cycling workout based on my recent training history.
|
||||||
|
|
||||||
|
{training_rules_section}
|
||||||
|
|
||||||
|
Please provide:
|
||||||
|
1. Analysis of my recent training pattern
|
||||||
|
2. Identified gaps or imbalances in my training
|
||||||
|
3. Specific workout recommendation for my next session
|
||||||
|
4. Target zones (power, heart rate, duration)
|
||||||
|
5. Rationale for the recommendation based on recent performance
|
||||||
Reference in New Issue
Block a user