mirror of
https://github.com/sstent/AICycling_mcp.git
synced 2025-12-05 23:51: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 logging
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from config import Config, load_config, create_sample_config
|
||||
from core_app import CyclingAnalyzerApp
|
||||
@@ -17,11 +19,8 @@ class CLI:
|
||||
def __init__(self):
|
||||
self.app = None
|
||||
|
||||
async def run(self):
|
||||
async def run(self, args):
|
||||
"""Main CLI loop"""
|
||||
print("Cycling Workout Analyzer")
|
||||
print("=" * 40)
|
||||
|
||||
# Setup configuration
|
||||
try:
|
||||
config = self._setup_config()
|
||||
@@ -33,9 +32,11 @@ class CLI:
|
||||
# Initialize app
|
||||
await self.app.initialize()
|
||||
|
||||
if args.command:
|
||||
await self._handle_command(args)
|
||||
else:
|
||||
# Show initial status
|
||||
self._show_status()
|
||||
|
||||
await self._show_status()
|
||||
# Main loop
|
||||
await self._main_loop()
|
||||
|
||||
@@ -67,10 +68,10 @@ class CLI:
|
||||
|
||||
return config
|
||||
|
||||
def _show_status(self):
|
||||
async def _show_status(self):
|
||||
"""Show application status"""
|
||||
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"- Cached data keys: {list(self.app.get_cached_data().keys())}")
|
||||
|
||||
@@ -100,7 +101,7 @@ class CLI:
|
||||
elif choice == "3":
|
||||
await self._enhanced_analysis()
|
||||
elif choice == "4":
|
||||
self._list_tools()
|
||||
await self._list_tools()
|
||||
elif choice == "5":
|
||||
self._list_templates()
|
||||
elif choice == "6":
|
||||
@@ -184,9 +185,9 @@ class CLI:
|
||||
print("="*50)
|
||||
print(result)
|
||||
|
||||
def _list_tools(self):
|
||||
async def _list_tools(self):
|
||||
"""List available tools"""
|
||||
tools = self.app.list_available_tools()
|
||||
tools = await self.app.list_available_tools()
|
||||
if tools:
|
||||
self.app.mcp_client.print_tools()
|
||||
else:
|
||||
@@ -247,10 +248,21 @@ Weekly Structure:
|
||||
f.write(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():
|
||||
"""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()
|
||||
await cli.run()
|
||||
await cli.run(args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
20
core_app.py
20
core_app.py
@@ -70,7 +70,7 @@ class CyclingAnalyzerApp:
|
||||
act for act in activities
|
||||
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
|
||||
|
||||
@@ -80,13 +80,13 @@ class CyclingAnalyzerApp:
|
||||
|
||||
# Prepare context data
|
||||
context = {
|
||||
"user_profile": self.cache_manager.get("user_profile", {}),
|
||||
"recent_activities": self.cache_manager.get("recent_activities", []),
|
||||
"last_cycling_details": self.cache_manager.get("last_cycling_details", {}),
|
||||
"user_info": self.cache_manager.get("user_profile", {}),
|
||||
"activity_summary": self.cache_manager.get("last_cycling_details", {}),
|
||||
**kwargs
|
||||
}
|
||||
|
||||
# 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)
|
||||
|
||||
# Call LLM
|
||||
@@ -122,9 +122,9 @@ class CyclingAnalyzerApp:
|
||||
|
||||
# Utility methods
|
||||
|
||||
def list_available_tools(self) -> list:
|
||||
async def list_available_tools(self) -> list:
|
||||
"""Get list of available MCP tools"""
|
||||
return self.mcp_client.list_tools()
|
||||
return await self.mcp_client.list_tools()
|
||||
|
||||
def list_templates(self) -> list:
|
||||
"""Get list of available templates"""
|
||||
@@ -145,13 +145,13 @@ async def main():
|
||||
await app.initialize()
|
||||
|
||||
# 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()))
|
||||
|
||||
# Run analysis
|
||||
analysis = await app.analyze_workout("analyze_last_workout",
|
||||
training_rules="Sample rules")
|
||||
print("Analysis:", analysis[:200] + "...")
|
||||
# analysis = await app.analyze_workout("analyze_last_workout",
|
||||
# training_rules="Sample rules")
|
||||
# print("Analysis:", analysis[:200] + "...")
|
||||
|
||||
except Exception as 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:
|
||||
section_content = self.load_template(section_file)
|
||||
# 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)
|
||||
except (FileNotFoundError, KeyError) as e:
|
||||
except (FileNotFoundError, KeyError, ValueError) as 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
|
||||
|
||||
|
||||
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