Skip to content

MaverickHQ/FitnessCore

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

FitnessCore

A serverless AI fitness coaching assistant delivered via Telegram β€” built on AWS AgentCore, MiniMax-M2.7, and a fully event-driven Lambda architecture.

FitnessCore lets users track daily nutrition and workouts through natural conversation. The agent understands free-form text, remembers what you told it in previous sessions, chains multiple tool calls to answer complex questions, and sends scheduled morning and evening reminders β€” all without any persistent servers.


Features

Feature How it works
Natural language logging "I just had 200g chicken and did a weightlifting session" β†’ protein and workout logged in one message
Daily summary /today or "how did I do today?" β†’ formatted breakdown of all logged metrics
Weekly summary /week β†’ 7-day totals, averages, workout count, best deficit day
Calorie burndown /remaining β†’ progress bar, % complete, and days-to-goal estimate at current pace
Profile management View and update current weight and target weight at any time
Conversational memory Facts stated in one session ("I'm vegetarian") are recalled in future sessions automatically
Complex reasoning "How close am I to my goal and what should I focus on this week?" β†’ multi-tool chain, personalised answer
Scheduled reminders Morning (8am UTC) motivation and evening (8pm UTC) log-completion check
Multi-user All data scoped by Telegram chat_id; any number of users supported without rearchitecting

Architecture

Telegram Users
      β”‚
      β”‚ HTTPS Webhook
      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  API Gateway (HTTP API)         β”‚
β”‚  POST /webhook                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Lambda: webhook-handler        β”‚  Validates Telegram secret.
β”‚  Python 3.12 Β· timeout 10s      β”‚  Enqueues message to SQS.
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  Returns 200 in < 3s.
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  SQS: agent-queue               β”‚  Standard queue.
β”‚  DLQ after 3 failures           β”‚  Visibility timeout 300s.
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Lambda: agent-invoker          β”‚  SQS consumer.
β”‚  Python 3.12 Β· timeout 120s     β”‚  Runs onboarding wizard for new users.
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  Calls AgentCore for returning users.
                 β”‚
                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AWS AgentCore Runtime                                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Per-session microVM Β· isolated per user_id                β”‚  β”‚
β”‚  β”‚                                                            β”‚  β”‚
β”‚  β”‚  1. Retrieve long-term memories (AgentCore Memory)         β”‚  β”‚
β”‚  β”‚  2. Build system prompt with user context                  β”‚  β”‚
β”‚  β”‚  3. Call MiniMax-M2.7 with tool definitions                β”‚  β”‚
β”‚  β”‚  4. AgentCore Gateway executes tool calls server-side      β”‚  β”‚
β”‚  β”‚  5. Feed results back β†’ repeat until end_turn              β”‚  β”‚
β”‚  β”‚  6. Save conversation turn to AgentCore Memory             β”‚  β”‚
β”‚  β”‚  7. Return final text response                             β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚                    β”‚                  β”‚                            β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚   β”‚  AgentCore Memory  β”‚  β”‚  AgentCore Gateway (MCP)            β”‚ β”‚
β”‚   β”‚                    β”‚  β”‚                                     β”‚ β”‚
β”‚   β”‚  Short-term:       β”‚  β”‚  Tools (server-side execution):     β”‚ β”‚
β”‚   β”‚   session turns    β”‚  β”‚  β€’ log_metric                       β”‚ β”‚
β”‚   β”‚                    β”‚  β”‚  β€’ get_summary                      β”‚ β”‚
β”‚   β”‚  Long-term:        β”‚  β”‚  β€’ get_or_update_profile            β”‚ β”‚
β”‚   β”‚   user facts       β”‚  β”‚  β€’ get_progress                     β”‚ β”‚
β”‚   β”‚   preferences      β”‚  β”‚  β€’ update_user_memory               β”‚ β”‚
β”‚   β”‚   patterns         β”‚  β”‚  β€’ onboard_user                     β”‚ β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚ Response text
                           β–Ό
              Lambda: agent-invoker β†’ Telegram sendMessage

EventBridge Scheduler
  β”œβ”€β”€ cron(0 8 * * ? *)   β†’ Lambda: notification-dispatcher  [morning nudge]
  └── cron(0 20 * * ? *)  β†’ Lambda: notification-dispatcher  [evening check-in]

Tech Stack

Layer Technology Notes
Frontend Telegram Bot API Webhook-based; no polling
API AWS API Gateway (HTTP API) Routes webhook POST to Lambda
Compute AWS Lambda (Python 3.12) ARM64, on-demand, per-function IAM roles
Agent runtime AWS AgentCore Runtime Per-session microVM; no persistent compute
Agent memory AWS AgentCore Memory Short-term (session turns) + long-term (semantic facts)
Tool server AWS AgentCore Gateway (MCP) Server-side tool execution via MCP protocol
LLM MiniMax-M2.7 Anthropic-compatible endpoint; no extra translation layer
Database Amazon DynamoDB (on-demand) Two tables; always scoped by user_id partition key
Queue Amazon SQS Decouples webhook ack from agent processing
Scheduler Amazon EventBridge Scheduler Morning and evening cron triggers
Secrets AWS SSM Parameter Store All API keys stored as SecureString; fetched at runtime
IaC AWS SAM Single template.yaml defines all infrastructure
Observability CloudWatch Logs + Alarms + X-Ray Dashboard, 3 alarms, 30-day log retention

Project Structure

fitnesscore/
β”œβ”€β”€ template.yaml                    ← AWS SAM β€” all infrastructure in one file
β”œβ”€β”€ samconfig.toml.example           ← deployment config template (copy β†’ samconfig.toml)
β”œβ”€β”€ pytest.ini
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ webhook/                     ← Lambda: Telegram webhook receiver
β”‚   β”‚   β”œβ”€β”€ handler.py               ← validates secret, enqueues to SQS
β”‚   β”‚   └── requirements.txt
β”‚   β”œβ”€β”€ agent-invoker/               ← Lambda: SQS consumer β†’ AgentCore
β”‚   β”‚   β”œβ”€β”€ handler.py               ← onboarding wizard + AgentCore invocation
β”‚   β”‚   └── requirements.txt
β”‚   β”œβ”€β”€ agent/                       ← AgentCore agent definition
β”‚   β”‚   β”œβ”€β”€ agent.py                 ← MiniMax client, memory, tool loop
β”‚   β”‚   β”œβ”€β”€ system_prompt.md         ← agent persona and instructions (single source of truth)
β”‚   β”‚   β”œβ”€β”€ Dockerfile               ← container image for ECR
β”‚   β”‚   └── requirements.txt
β”‚   β”œβ”€β”€ tools/                       ← AgentCore Gateway tool implementations
β”‚   β”‚   β”œβ”€β”€ fitness_log.py           ← log_metric tool
β”‚   β”‚   β”œβ”€β”€ fitness_summary.py       ← get_summary tool
β”‚   β”‚   β”œβ”€β”€ fitness_profile.py       ← get_or_update_profile tool
β”‚   β”‚   β”œβ”€β”€ fitness_burndown.py      ← get_progress tool
β”‚   β”‚   β”œβ”€β”€ user_memory.py           ← update_user_memory tool
β”‚   β”‚   └── onboard_user.py          ← onboard_user tool
β”‚   β”œβ”€β”€ tools-gateway/               ← Lambda: AgentCore Gateway dispatcher
β”‚   β”‚   └── handler.py
β”‚   └── notifications/               ← Lambda: EventBridge-triggered reminders
β”‚       └── dispatcher.py
β”‚
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ conftest.py                  ← shared fixtures (mocked DynamoDB, SSM)
β”‚   β”œβ”€β”€ unit/                        ← 162 unit tests; moto mocks all AWS calls
β”‚   └── integration/                 ← end-to-end tests against deployed stack
β”‚       β”œβ”€β”€ integration_config.py.example   ← copy and fill in your values
β”‚       β”œβ”€β”€ test_e2e.py              ← webhook β†’ SQS β†’ Lambda β†’ DynamoDB pipeline
β”‚       β”œβ”€β”€ test_agent.py            ← AgentCore memory and tool-chain tests
β”‚       └── test_reasoning.py        ← multi-step reasoning scenarios
β”‚
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ ARCHITECTURE.md              ← detailed architecture and design decisions
β”‚   β”œβ”€β”€ DATA_MODEL.md                ← DynamoDB schemas, validation rules, examples
β”‚   └── RUNBOOK.md                   ← deploy, rollback, logs, secret rotation, troubleshooting
β”‚
β”œβ”€β”€ infra/
β”‚   β”œβ”€β”€ iam.yaml                     ← IAM roles reference (least-privilege, per-Lambda)
β”‚   β”œβ”€β”€ dynamodb.yaml                ← DynamoDB table definitions
β”‚   └── ssm-params.sh                ← SSM parameter names reference (no values)
β”‚
└── scripts/
    β”œβ”€β”€ setup-dev.sh                 ← install local dev dependencies
    β”œβ”€β”€ deploy.sh                    ← pytest β†’ sam build β†’ sam deploy pipeline
    └── seed-test-data.sh            ← seed DynamoDB with realistic test fixtures

Getting Started

Prerequisites

  • Python 3.12 β€” python3 --version
  • AWS CLI v2 β€” install guide
  • AWS SAM CLI β‰₯ 1.100 β€” install guide
  • Docker β€” required for SAM builds with Lambda container images
  • An AWS account with permissions to create Lambda, DynamoDB, SQS, API Gateway, IAM, SSM, EventBridge, and AgentCore resources
  • A Telegram bot token β€” create one via @BotFather
  • A MiniMax API key β€” from platform.minimaxi.com

1. Clone and set up local environment

git clone https://github.com/YOUR_USERNAME/fitnesscore.git
cd fitnesscore
bash scripts/setup-dev.sh
source .venv/bin/activate

2. Configure AWS credentials

aws configure --profile fitnesscore-dev
# Enter: Access Key ID, Secret Access Key, region (eu-west-1), output (json)

3. Store secrets in SSM Parameter Store

# Telegram bot token (from @BotFather)
aws ssm put-parameter \
  --name /fitnesscore/telegram/bot_token \
  --value "YOUR_BOT_TOKEN" \
  --type SecureString \
  --profile fitnesscore-dev --region eu-west-1

# Webhook secret (generate a random string)
aws ssm put-parameter \
  --name /fitnesscore/telegram/webhook_secret \
  --value "$(openssl rand -hex 32)" \
  --type SecureString \
  --profile fitnesscore-dev --region eu-west-1

# MiniMax API key
aws ssm put-parameter \
  --name /fitnesscore/minimax/api_key \
  --value "YOUR_MINIMAX_API_KEY" \
  --type SecureString \
  --profile fitnesscore-dev --region eu-west-1

4. Configure SAM deployment

cp samconfig.toml.example samconfig.toml
# Edit samconfig.toml: set your AWS profile name

5. Set up AgentCore

AgentCore Runtime, Memory, and Gateway must be provisioned before deployment. See docs/ARCHITECTURE.md for the full AgentCore setup steps, including:

  • Creating an AgentCore Runtime (container image from ECR)
  • Creating AgentCore Memory with a user preferences strategy
  • Creating an AgentCore Gateway and registering the 6 fitness tools
  • Storing the agent runtime ARN and memory IDs in SSM:
aws ssm put-parameter \
  --name /fitnesscore/agentcore/agent_id \
  --value "YOUR_AGENT_RUNTIME_ARN" \
  --type String \
  --profile fitnesscore-dev --region eu-west-1

6. Deploy

# Runs: pytest tests/unit β†’ sam build β†’ sam deploy
bash scripts/deploy.sh

On success, the webhook URL is printed in the CloudFormation outputs. Register it with Telegram:

WEBHOOK_SECRET=$(aws ssm get-parameter \
  --name /fitnesscore/telegram/webhook_secret \
  --with-decryption --query Parameter.Value --output text \
  --profile fitnesscore-dev --region eu-west-1)

BOT_TOKEN=$(aws ssm get-parameter \
  --name /fitnesscore/telegram/bot_token \
  --with-decryption --query Parameter.Value --output text \
  --profile fitnesscore-dev --region eu-west-1)

curl -X POST "https://api.telegram.org/bot${BOT_TOKEN}/setWebhook" \
  -H "Content-Type: application/json" \
  -d "{
    \"url\": \"YOUR_WEBHOOK_URL_FROM_OUTPUTS\",
    \"secret_token\": \"${WEBHOOK_SECRET}\",
    \"allowed_updates\": [\"message\"]
  }"

7. Verify

Send /start to your bot in Telegram. You should be greeted by the onboarding wizard within a few seconds.


Running Tests

Unit tests (no AWS required)

pytest tests/unit -v
# 162 tests Β· moto mocks all DynamoDB and SSM calls

Integration tests (requires deployed stack)

# 1. Seed test data
bash scripts/seed-test-data.sh

# 2. Configure your test values
cp tests/integration/integration_config.py.example tests/integration/integration_config.py
# Edit integration_config.py with your ARNs and test user ID

# 3. Run
AWS_PROFILE=your-profile pytest tests/integration/test_e2e.py tests/integration/test_agent.py -v -s

Data Model

Two DynamoDB tables, both with on-demand billing and at-rest encryption:

fitnesscore-users β€” one record per registered user

Attribute Type Description
user_id (PK) String Telegram chat_id
first_name String Telegram first name
current_weight Number Current weight in kg
target_weight Number Goal weight in kg
voice_enabled Boolean Daily voice note opt-in
video_enabled Boolean Weekly video opt-in
active Boolean Include in notification fan-out
registered_at String ISO8601 UTC timestamp

fitnesscore-daily-logs β€” one record per user per calendar day

Attribute Type Description
user_id (PK) String Telegram chat_id
date (SK) String YYYY-MM-DD
protein Number Grams of protein (0–500)
calories Number Kcal intake (0–10,000)
calories_burned Number Kcal burned through exercise (0–5,000)
deficit Number Calorie deficit, can be negative (βˆ’5,000–5,000)
weightlifting Boolean Weightlifting session completed
cycling Boolean Cycling session completed

Full schemas, access patterns, and validation rules in docs/DATA_MODEL.md.


AgentCore Tools

Six tools registered with AgentCore Gateway. Each is a pure function β€” one DynamoDB operation or one calculation. The agent never sees user_id in tool schemas; it is always injected server-side.

Tool What it does
log_metric Write protein/calories/burn/deficit/workout to fitnesscore-daily-logs
get_summary Read today's or this week's metrics from fitnesscore-daily-logs
get_or_update_profile Read or write user profile in fitnesscore-users
get_progress Calculate calorie burndown progress %, kcal remaining, days to goal
update_user_memory Write a long-term user fact to AgentCore Memory
onboard_user Create a new user record in fitnesscore-users

Security

  • No secrets in code β€” all API keys and tokens are fetched from SSM Parameter Store at runtime
  • Webhook validation β€” every request validated against X-Telegram-Bot-Api-Secret-Token using constant-time comparison
  • User isolation β€” every DynamoDB query includes user_id as the partition key; no cross-user data access is possible
  • AgentCore session isolation β€” one microVM per user session; memory is scoped per actor_id
  • Least-privilege IAM β€” each Lambda has a dedicated role with only the specific actions it needs on specific resources
  • Encryption at rest β€” DynamoDB and SQS both have server-side encryption enabled

Cost Estimate

Phase 1 runs comfortably within AWS free tier limits for Lambda and SQS. The main costs are AgentCore and MiniMax.

Scale Monthly estimate
1 active user ~$4–7
10 active users ~$29–34
50 active users ~$90–110

Detailed breakdown in docs/ARCHITECTURE.md.


Documentation

Document Contents
docs/ARCHITECTURE.md Full system design, component responsibilities, memory strategy, IAM design, cost model, key decisions
docs/DATA_MODEL.md DynamoDB schemas, attribute types, validation constraints, access patterns
docs/RUNBOOK.md Deploy, rollback, log tailing, secret rotation, troubleshooting guide, known limitations

Observability

  • CloudWatch Dashboard: FitnessCore-Operations β€” Lambda invocations/errors/duration, SQS depth, DLQ count, DynamoDB capacity
  • CloudWatch Alarms: DLQ > 0 messages, webhook error rate > 5%, agent-invoker p99 latency > 90s β†’ SNS topic
  • X-Ray tracing: Active tracing enabled on all Lambda functions
  • Log retention: 30 days on all Lambda log groups

About

Serverless AI fitness coaching assistant built on AWS AgentCore, delivered via Telegram. Logs daily metrics, tracks calorie burndown toward a weight goal, and uses conversational memory across sessions.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages