Stream C: Multi-provider AI coaching backend + 20-provider registry
AI coaching service (backend/app/services/ai_provider.py): - Two code paths: OpenAI-compatible (6 providers) + Anthropic (Claude) - Streaming SSE responses via httpx async - System prompt built from AGENT_GUIDE + live user context - Graceful error handling for all provider failures - Gemini adapter for Google's generateContent API Chat endpoint (backend/app/routers/ai_chat.py): - POST /api/ai/chat — SSE streaming response - GET /api/ai/status — check configured provider - History capped at 20 messages, content at 4K chars Provider registry expanded to 20 providers: - 9 device integrations (strava, polar, garmin, whoop, oura, coros, fitbit, withings, suunto) - 8 AI providers (openai, openrouter, huggingface, groq, ollama, claude, gemini, custom_ai) - 3 other (usda, nutritionix, telegram) - Categorized: device, ai_provider, nutrition, notifications - COROS: MCP bridge approach (no API integration needed) - Strava: AI/ML warning per their ToS Frontend ChatCoach.jsx to follow in next commit.
This commit is contained in:
parent
6e082ce58f
commit
9a8e6ce1eb
4 changed files with 555 additions and 27 deletions
|
|
@ -2,65 +2,213 @@
|
|||
from __future__ import annotations
|
||||
|
||||
PROVIDER_REGISTRY: dict[str, dict] = {
|
||||
|
||||
# ═══ Device Integrations ═══
|
||||
|
||||
"strava": {
|
||||
"name": "Strava",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://www.strava.com/oauth/authorize",
|
||||
"token_url": "https://www.strava.com/oauth/token",
|
||||
"scopes": "read,activity:read_all",
|
||||
"help_url": "https://developers.strava.com/",
|
||||
"help_text": "Create an app at developers.strava.com. Set the callback URL to {BASE_URL}/api/integrations/strava/callback",
|
||||
"warning": "Strava ToS prohibits use of their data for AI/ML. Activity sync works but AI coaching should not reference Strava data.",
|
||||
"has_webhook": False,
|
||||
"sync_service": "strava_sync",
|
||||
},
|
||||
"polar": {
|
||||
"name": "Polar",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://flow.polar.com/oauth2/authorization",
|
||||
"token_url": "https://polarremote.com/v2/oauth2/token",
|
||||
"help_url": "https://admin.polaraccesslink.com/",
|
||||
"help_text": "Register at admin.polaraccesslink.com",
|
||||
"has_webhook": False,
|
||||
"sync_service": "polar_sync",
|
||||
},
|
||||
"garmin": {
|
||||
"name": "Garmin Connect",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://connect.garmin.com/oauthConfirm",
|
||||
"token_url": "https://connectapi.garmin.com/oauth-service/oauth/token",
|
||||
"scopes": "HEALTH,ACTIVITY",
|
||||
"help_url": "https://developer.garmin.com/gc-developer-program/",
|
||||
"help_text": "Apply at developer.garmin.com — approval takes 1-4 weeks",
|
||||
},
|
||||
"fitbit": {
|
||||
"name": "Fitbit",
|
||||
"type": "oauth2",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://www.fitbit.com/oauth2/authorize",
|
||||
"token_url": "https://api.fitbit.com/oauth2/token",
|
||||
"help_url": "https://dev.fitbit.com/",
|
||||
"help_text": "Register at dev.fitbit.com",
|
||||
},
|
||||
"withings": {
|
||||
"name": "Withings",
|
||||
"type": "oauth2",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"help_url": "https://developer.withings.com/",
|
||||
"help_text": "Register at developer.withings.com — body metrics, scales, blood pressure",
|
||||
"help_text": "Apply at developer.garmin.com — approval ~2 business days, requires a business entity.",
|
||||
"has_webhook": True,
|
||||
"sync_service": "garmin_sync",
|
||||
},
|
||||
"whoop": {
|
||||
"name": "WHOOP",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://api.prod.whoop.com/oauth/oauth2/auth",
|
||||
"token_url": "https://api.prod.whoop.com/oauth/oauth2/token",
|
||||
"scopes": "read:workout,read:recovery,read:sleep,read:profile,offline",
|
||||
"help_url": "https://developer.whoop.com/",
|
||||
"help_text": "Apply at developer.whoop.com — recovery, strain, sleep",
|
||||
"help_text": "Self-serve at developer.whoop.com. Requires WHOOP device. <10 users without app approval.",
|
||||
"has_webhook": True,
|
||||
"sync_service": "whoop_sync",
|
||||
},
|
||||
"oura": {
|
||||
"name": "Oura Ring",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://cloud.ouraring.com/oauth/authorize",
|
||||
"token_url": "https://api.ouraring.com/oauth/token",
|
||||
"scopes": "daily,workout,heartrate,session",
|
||||
"help_url": "https://cloud.ouraring.com/v2/docs",
|
||||
"help_text": "Register at cloud.ouraring.com — sleep, readiness, HRV",
|
||||
"help_text": "Register at cloud.ouraring.com — self-serve, immediate access.",
|
||||
"has_webhook": True,
|
||||
"sync_service": "oura_sync",
|
||||
},
|
||||
"coros": {
|
||||
"name": "COROS (via MCP)",
|
||||
"type": "mcp_bridge",
|
||||
"category": "device",
|
||||
"fields": [],
|
||||
"help_url": "https://support.coros.com/hc/en-us/articles/17085887816340",
|
||||
"help_text": "COROS has an official MCP server. Add it alongside Diligence in your AI agent config.",
|
||||
"has_webhook": False,
|
||||
"sync_service": None,
|
||||
},
|
||||
"fitbit": {
|
||||
"name": "Fitbit",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"auth_url": "https://www.fitbit.com/oauth2/authorize",
|
||||
"token_url": "https://api.fitbit.com/oauth2/token",
|
||||
"help_url": "https://dev.fitbit.com/",
|
||||
"help_text": "Register at dev.fitbit.com via Google Cloud Console.",
|
||||
"has_webhook": True,
|
||||
"sync_service": None,
|
||||
},
|
||||
"withings": {
|
||||
"name": "Withings",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"help_url": "https://developer.withings.com/",
|
||||
"help_text": "Register at developer.withings.com — body metrics, scales, blood pressure.",
|
||||
"has_webhook": False,
|
||||
"sync_service": None,
|
||||
},
|
||||
"suunto": {
|
||||
"name": "Suunto",
|
||||
"type": "oauth2",
|
||||
"category": "device",
|
||||
"fields": ["client_id", "client_secret"],
|
||||
"help_url": "https://apizone.suunto.com/",
|
||||
"help_text": "Apply at Suunto developer portal — contract required, ~2 week response.",
|
||||
"has_webhook": True,
|
||||
"sync_service": None,
|
||||
},
|
||||
|
||||
# ═══ AI Coaching Providers ═══
|
||||
|
||||
"openai": {
|
||||
"name": "OpenAI",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://api.openai.com/v1",
|
||||
"api_format": "openai",
|
||||
"help_url": "https://platform.openai.com/api-keys",
|
||||
"help_text": "Get API key from platform.openai.com. Models: gpt-4o, gpt-4o-mini.",
|
||||
"default_model": "gpt-4o-mini",
|
||||
},
|
||||
"openrouter": {
|
||||
"name": "OpenRouter",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://openrouter.ai/api/v1",
|
||||
"api_format": "openai",
|
||||
"help_url": "https://openrouter.ai/keys",
|
||||
"help_text": "One key, 300+ models. 26 free models. Pay-as-you-go, no subscription.",
|
||||
"default_model": "openai/gpt-4o-mini",
|
||||
},
|
||||
"huggingface": {
|
||||
"name": "Hugging Face",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://router.huggingface.co/v1",
|
||||
"api_format": "openai",
|
||||
"help_url": "https://huggingface.co/settings/tokens",
|
||||
"help_text": "Free HF token. Thousands of open-source models via 20+ inference providers.",
|
||||
"default_model": "meta-llama/Llama-3.3-70B-Instruct",
|
||||
},
|
||||
"groq": {
|
||||
"name": "Groq",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://api.groq.com/openai/v1",
|
||||
"api_format": "openai",
|
||||
"help_url": "https://console.groq.com/keys",
|
||||
"help_text": "Ultra-fast inference. Free tier available. Also used for program research.",
|
||||
"default_model": "llama-3.3-70b-versatile",
|
||||
},
|
||||
"ollama": {
|
||||
"name": "Ollama (Local)",
|
||||
"type": "endpoint",
|
||||
"category": "ai_provider",
|
||||
"fields": ["endpoint_url"],
|
||||
"base_url": "http://host.docker.internal:11434/v1",
|
||||
"api_format": "openai",
|
||||
"help_url": "https://ollama.com/",
|
||||
"help_text": "Run LLMs locally. No API key, no cost. Default: http://host.docker.internal:11434",
|
||||
"default_model": "llama3.1",
|
||||
},
|
||||
"claude": {
|
||||
"name": "Anthropic Claude",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://api.anthropic.com/v1",
|
||||
"api_format": "anthropic",
|
||||
"help_url": "https://console.anthropic.com/",
|
||||
"help_text": "Get API key from console.anthropic.com. Models: claude-sonnet-4-6, claude-opus-4-8.",
|
||||
"default_model": "claude-sonnet-4-6",
|
||||
},
|
||||
"gemini": {
|
||||
"name": "Google Gemini",
|
||||
"type": "api_key",
|
||||
"category": "ai_provider",
|
||||
"fields": ["api_key"],
|
||||
"base_url": "https://generativelanguage.googleapis.com/v1beta",
|
||||
"api_format": "gemini",
|
||||
"help_url": "https://aistudio.google.com/apikey",
|
||||
"help_text": "Get API key from AI Studio. Models: gemini-2.0-flash, gemini-2.5-pro.",
|
||||
"default_model": "gemini-2.0-flash",
|
||||
},
|
||||
"custom_ai": {
|
||||
"name": "Custom (OpenAI-compatible)",
|
||||
"type": "endpoint",
|
||||
"category": "ai_provider",
|
||||
"fields": ["endpoint_url", "api_key"],
|
||||
"api_format": "openai",
|
||||
"help_url": "",
|
||||
"help_text": "Any server with an OpenAI-compatible /v1/chat/completions endpoint (vLLM, TGI, LiteLLM).",
|
||||
"default_model": "",
|
||||
},
|
||||
|
||||
# ═══ Nutrition & Data ═══
|
||||
|
||||
"usda": {
|
||||
"name": "USDA FoodData Central",
|
||||
"type": "api_key",
|
||||
"category": "nutrition",
|
||||
"fields": ["api_key"],
|
||||
"help_url": "https://fdc.nal.usda.gov/api-key-signup",
|
||||
"help_text": "Free API key, no approval needed. 400K+ foods with research-grade nutrition data.",
|
||||
|
|
@ -68,22 +216,20 @@ PROVIDER_REGISTRY: dict[str, dict] = {
|
|||
"nutritionix": {
|
||||
"name": "Nutritionix",
|
||||
"type": "api_key",
|
||||
"category": "nutrition",
|
||||
"fields": ["app_id", "api_key"],
|
||||
"help_url": "https://developer.nutritionix.com/",
|
||||
"help_text": "Free tier: 1K calls/month. Natural language food parsing.",
|
||||
},
|
||||
"groq": {
|
||||
"name": "Groq (Program Research)",
|
||||
"type": "api_key",
|
||||
"fields": ["api_key"],
|
||||
"help_url": "https://console.groq.com/keys",
|
||||
"help_text": "Free API key. Enables AI-powered workout program extraction.",
|
||||
},
|
||||
|
||||
# ═══ Notifications ═══
|
||||
|
||||
"telegram": {
|
||||
"name": "Telegram Notifications",
|
||||
"type": "api_key",
|
||||
"category": "notifications",
|
||||
"fields": ["bot_token", "chat_id"],
|
||||
"help_url": "https://core.telegram.org/bots#botfather",
|
||||
"help_text": "Create a bot via @BotFather, then get your chat ID",
|
||||
"help_text": "Create a bot via @BotFather, then get your chat ID.",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue