2025-09-26 07:17:35 -07:00
2025-09-26 07:17:35 -07:00
2025-09-26 07:17:35 -07:00
2025-09-26 07:17:35 -07:00
2025-09-26 07:17:35 -07:00
2025-09-26 06:52:27 -07:00
2025-09-26 06:52:27 -07:00
2025-09-25 18:42:01 -07:00
2025-09-26 06:52:27 -07:00
2025-09-26 06:52:27 -07:00
2025-09-24 16:29:16 -07:00
2025-09-26 06:52:27 -07:00
2025-09-26 07:17:35 -07:00
2025-09-25 09:04:37 -07:00

Cycling Workout Analyzer - Clean Architecture

A modular, extensible cycling workout analyzer built with a clean architecture that separates core concerns into focused modules.

Architecture Overview

The application is structured into distinct, focused modules:

├── core_app.py          # Main orchestrator
├── config.py            # Configuration management  
├── llm_client.py        # LLM interactions
├── mcp_client.py        # MCP server management
├── cache_manager.py     # Data caching with TTL
├── template_engine.py   # Template loading/rendering
├── cli_interface.py     # Command line interface
└── requirements.txt     # Dependencies

Core Features

🤖 LLM Integration

  • OpenRouter API support with multiple models
  • Both tool-enabled and tool-free analysis modes
  • Async request handling with timeouts

🔧 MCP Tool Management

  • Automatic MCP server discovery and connection
  • Tool listing and direct tool calling
  • Garth MCP server integration for Garmin data

💾 Smart Caching

  • TTL-based caching system
  • Pre-loading of common data (user profile, activities)
  • Specialized cycling data cache helpers

📝 Template System

  • Modular template structure
  • Section includes and variable substitution
  • Auto-creation of default templates

⚙️ Configuration

  • YAML config files with environment variable fallback
  • Automatic sample config generation
  • Extensible configuration structure

Quick Start

1. Install Dependencies

pip install -r requirements.txt

# Install MCP server for Garmin data
npm install -g garth-mcp-server

2. Configure

# Run once to create config.yaml
python cli_interface.py

# Edit config.yaml with your API keys

3. Run

python cli_interface.py

Usage Examples

Basic Analysis

from config import load_config
from core_app import CyclingAnalyzerApp

config = load_config()
app = CyclingAnalyzerApp(config)

await app.initialize()

# Analyze last workout
analysis = await app.analyze_workout("analyze_last_workout")
print(analysis)

# Get workout suggestion  
suggestion = await app.suggest_next_workout()
print(suggestion)

await app.cleanup()

Custom Analysis

# Enhanced analysis with tools
analysis = await app.enhanced_analysis(
    "performance_trends", 
    training_rules="Custom rules here"
)

# Check what tools are available
tools = app.list_available_tools()
for tool in tools:
    print(f"- {tool.name}: {tool.description}")

Cache Management

# Check cached data
cached = app.get_cached_data()
print("Cached keys:", list(cached.keys()))

# Cache custom data
app.cache_manager.set("custom_key", {"data": "value"}, ttl=600)

Configuration

config.yaml

# LLM Settings
openrouter_api_key: "your_api_key_here"
openrouter_model: "deepseek/deepseek-r1-0528:free"

# MCP Settings  
garth_token: "your_garth_token_here"
garth_mcp_server_path: "uvx"

# Application Settings
templates_dir: "templates"
rules_file: "rules.yaml"
cache_ttl: 300
log_level: "INFO"

Environment Variables

export OPENROUTER_API_KEY="your_key"
export GARTH_TOKEN="your_token"
export LOG_LEVEL="DEBUG"

Extension Points

Custom Analysis Types

# Add new analysis in core_app.py
async def custom_analysis(self, **kwargs) -> str:
    template = "workflows/custom_analysis.txt" 
    context = {"custom_data": kwargs}
    prompt = self.template_engine.render(template, **context)
    return await self.llm_client.generate(prompt)

Custom MCP Tools

# Add new tool support in mcp_client.py  
async def call_custom_tool(self, parameters: dict) -> dict:
    return await self.call_tool("custom_tool", parameters)

Custom Templates

Create new templates in templates/workflows/:

templates/
├── workflows/
│   ├── my_analysis.txt
│   └── custom_report.txt
├── base/
│   ├── data_sections/
│   └── analysis_frameworks/

Custom Cache Strategies

from cache_manager import CacheManager

class CustomCache(CacheManager):
    def cache_performance_data(self, data, athlete_id):
        self.set(f"performance_{athlete_id}", data, ttl=1800)

Architecture Benefits

Separation of Concerns

  • Config: Handles all configuration logic
  • LLM Client: Pure LLM interactions
  • MCP Client: Tool management only
  • Cache: Data persistence with TTL
  • Templates: Prompt composition
  • CLI: User interface

Extensibility

  • Easy to add new LLM providers
  • Plugin-style MCP tool additions
  • Template-based prompt customization
  • Configurable caching strategies

Testability

  • Each module has single responsibility
  • Clear interfaces between components
  • Mock-friendly async design
  • Dependency injection ready

Maintainability

  • Small, focused files
  • Clear naming conventions
  • Comprehensive logging
  • Error handling at boundaries

Advanced Features

Template Inheritance

Templates can include sections and inherit from base templates:

{activity_summary_section}  # Includes base/data_sections/activity_summary.txt
{assessment_points}         # Includes base/analysis_frameworks/assessment_points.txt

Dynamic Tool Selection

The app automatically detects available tools and adjusts functionality:

if await self.mcp_client.has_tool("hrv_data"):
    hrv_data = await self.mcp_client.call_tool("hrv_data", {})

Cache Warming

Common data is pre-loaded during initialization:

  • User profile (1 hour TTL)
  • Recent activities (15 min TTL)
  • Last cycling activity details (1 hour TTL)

Troubleshooting

MCP Connection Issues

# Check if garth-mcp-server is installed
which garth-mcp-server

# Test Garth token  
uvx garth login

Template Errors

# List available templates
python -c "from template_engine import TemplateEngine; print(TemplateEngine('templates').list_templates())"

# Check template variables
python -c "from template_engine import TemplateEngine; print(TemplateEngine('templates').get_template_info('workflows/analyze_last_workout.txt'))"

Cache Issues

# Clear cache
python -c "from cache_manager import CacheManager; CacheManager().clear()"

Contributing

The modular architecture makes contributions straightforward:

  1. New LLM Provider: Extend LLMClient
  2. New Data Source: Create new MCP client
  3. New Analysis: Add templates and methods
  4. New Interface: Create alternative to CLI
  5. New Cache Strategy: Extend CacheManager

Each module is independently testable and can be developed in isolation.

Description
No description provided
Readme 179 KiB
Languages
Python 100%