Spaces:
Sleeping
Sleeping
github-actions[bot]
commited on
Commit
Β·
b52102b
1
Parent(s):
14b666f
π€ Auto-deploy from GitHub (push) - d2954f9 - 2025-07-27 04:52:05 UTC
Browse files- .env.example +18 -3
- README.md +28 -7
- fitness_agent/app.py +214 -52
- fitness_agent/examples.py +100 -25
- fitness_agent/fitness_agent.py +261 -40
- fitness_agent/test_openai_models.py +151 -0
.env.example
CHANGED
|
@@ -1,12 +1,27 @@
|
|
| 1 |
# Copy this file to .env and fill in your API keys
|
| 2 |
# Required for the fitness agent to work
|
| 3 |
|
| 4 |
-
#
|
|
|
|
| 5 |
OPENAI_API_KEY=your_openai_api_key_here
|
| 6 |
|
| 7 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
# ANTHROPIC_API_KEY=your_anthropic_key_here
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
# Optional: Hugging Face token for some features
|
| 12 |
# HF_TOKEN=your_huggingface_token_here
|
|
|
|
| 1 |
# Copy this file to .env and fill in your API keys
|
| 2 |
# Required for the fitness agent to work
|
| 3 |
|
| 4 |
+
# === OPTION 1: OpenAI Models ===
|
| 5 |
+
# For GPT-4o, GPT-4 Turbo, GPT-3.5, o1-preview, o1-mini, o3-mini
|
| 6 |
OPENAI_API_KEY=your_openai_api_key_here
|
| 7 |
|
| 8 |
+
# === OPTION 2: Anthropic Models ===
|
| 9 |
+
# For Claude-4, Claude-3.7, Claude-3.5, Claude-3 models
|
| 10 |
+
ANTHROPIC_API_KEY=your_anthropic_key_here
|
| 11 |
+
|
| 12 |
+
# === OPTION 3: Both Providers ===
|
| 13 |
+
# Set both keys to have access to all models
|
| 14 |
+
# OPENAI_API_KEY=your_openai_api_key_here
|
| 15 |
# ANTHROPIC_API_KEY=your_anthropic_key_here
|
| 16 |
+
|
| 17 |
+
# Optional: Set default model (will use claude-3.5-haiku if not set)
|
| 18 |
+
# AI_MODEL=gpt-4o-mini
|
| 19 |
+
# AI_MODEL=claude-3.5-sonnet
|
| 20 |
+
# AI_MODEL=gpt-4o
|
| 21 |
+
|
| 22 |
+
# Alternative environment variable names (for compatibility)
|
| 23 |
+
# ANTHROPIC_MODEL=claude-3.5-haiku
|
| 24 |
+
# OPENAI_MODEL=gpt-4o-mini
|
| 25 |
|
| 26 |
# Optional: Hugging Face token for some features
|
| 27 |
# HF_TOKEN=your_huggingface_token_here
|
README.md
CHANGED
|
@@ -12,16 +12,31 @@ license: mit
|
|
| 12 |
|
| 13 |
# ποΈββοΈ Fitness AI Assistant
|
| 14 |
|
| 15 |
-
Your personal fitness companion for workout plans, meal planning, and fitness guidance powered by Anthropic
|
| 16 |
|
| 17 |
## β¨ Features
|
| 18 |
|
| 19 |
- **ποΈ Personalized Workout Plans**: Custom routines based on your fitness level and goals
|
| 20 |
-
- **π₯ Meal Planning**: Tailored nutrition plans for weight loss, muscle gain, or general health
|
| 21 |
- **π‘ Fitness Guidance**: Expert advice on exercises, form, and best practices
|
| 22 |
-
- **π€ Multiple AI
|
|
|
|
| 23 |
- **π¬ Interactive Chat**: Conversational interface with memory and context
|
| 24 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
## π Quick Start
|
| 27 |
|
|
@@ -34,16 +49,22 @@ cd fitness-app
|
|
| 34 |
# Install dependencies
|
| 35 |
pip install -r requirements.txt
|
| 36 |
|
| 37 |
-
# Set up environment
|
| 38 |
cp .env.example .env
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
# Launch the app
|
| 42 |
python fitness_agent/app.py
|
| 43 |
```
|
| 44 |
|
| 45 |
### Option 2: Use the Interface
|
| 46 |
-
1. **Select your AI model** from the dropdown
|
|
|
|
|
|
|
| 47 |
2. **Start chatting** about your fitness goals
|
| 48 |
3. **Be specific** about your level, equipment, and preferences
|
| 49 |
4. **Get personalized plans** and ask follow-up questions
|
|
|
|
| 12 |
|
| 13 |
# ποΈββοΈ Fitness AI Assistant
|
| 14 |
|
| 15 |
+
Your personal fitness companion for workout plans, meal planning, and fitness guidance powered by **multiple AI providers** - choose between Anthropic Claude and OpenAI GPT models!
|
| 16 |
|
| 17 |
## β¨ Features
|
| 18 |
|
| 19 |
- **ποΈ Personalized Workout Plans**: Custom routines based on your fitness level and goals
|
| 20 |
+
- **π₯ Meal Planning**: Tailored nutrition plans for weight loss, muscle gain, or general health
|
| 21 |
- **π‘ Fitness Guidance**: Expert advice on exercises, form, and best practices
|
| 22 |
+
- **π€ Multiple AI Providers**: Choose from Anthropic Claude OR OpenAI GPT models
|
| 23 |
+
- **β‘ Model Flexibility**: Switch between models anytime for different capabilities
|
| 24 |
- **π¬ Interactive Chat**: Conversational interface with memory and context
|
| 25 |
+
- **π Real-time Streaming**: See responses generated live
|
| 26 |
+
|
| 27 |
+
## π€ Supported AI Models
|
| 28 |
+
|
| 29 |
+
### π΅ Anthropic Claude Models
|
| 30 |
+
- **Claude-4**: claude-4-opus, claude-4-sonnet (Premium, most capable)
|
| 31 |
+
- **Claude-3.7**: claude-3.7-sonnet (Extended thinking)
|
| 32 |
+
- **Claude-3.5**: claude-3.5-sonnet, claude-3.5-haiku (Balanced, fast)
|
| 33 |
+
- **Claude-3**: claude-3-haiku (Cost-effective)
|
| 34 |
+
|
| 35 |
+
### π’ OpenAI GPT Models
|
| 36 |
+
- **GPT-4o**: gpt-4o, gpt-4o-mini (Latest with vision)
|
| 37 |
+
- **GPT-4**: gpt-4-turbo (Large context window)
|
| 38 |
+
- **GPT-3.5**: gpt-3.5-turbo (Fast and economical)
|
| 39 |
+
- **Reasoning**: o1-preview, o1-mini, o3-mini (Advanced reasoning)
|
| 40 |
|
| 41 |
## π Quick Start
|
| 42 |
|
|
|
|
| 49 |
# Install dependencies
|
| 50 |
pip install -r requirements.txt
|
| 51 |
|
| 52 |
+
# Set up environment - choose your provider(s)
|
| 53 |
cp .env.example .env
|
| 54 |
+
|
| 55 |
+
# Edit .env and add your API key(s):
|
| 56 |
+
# For OpenAI models: OPENAI_API_KEY=your_openai_key_here
|
| 57 |
+
# For Anthropic models: ANTHROPIC_API_KEY=your_anthropic_key_here
|
| 58 |
+
# For both providers: Set both keys!
|
| 59 |
|
| 60 |
# Launch the app
|
| 61 |
python fitness_agent/app.py
|
| 62 |
```
|
| 63 |
|
| 64 |
### Option 2: Use the Interface
|
| 65 |
+
1. **Select your AI provider and model** from the dropdown
|
| 66 |
+
- π΅ Anthropic models for detailed analysis and safety
|
| 67 |
+
- π’ OpenAI models for familiar interface and vision capabilities
|
| 68 |
2. **Start chatting** about your fitness goals
|
| 69 |
3. **Be specific** about your level, equipment, and preferences
|
| 70 |
4. **Get personalized plans** and ask follow-up questions
|
fitness_agent/app.py
CHANGED
|
@@ -113,16 +113,31 @@ def change_model(new_model: str) -> str:
|
|
| 113 |
|
| 114 |
# Test if we can create an agent with this model (basic validation)
|
| 115 |
try:
|
|
|
|
| 116 |
test_agent = FitnessAgent(new_model)
|
|
|
|
| 117 |
# If we get here, the model is likely available
|
| 118 |
except Exception as model_error:
|
| 119 |
-
#
|
| 120 |
error_str = str(model_error)
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
recommended = ", ".join(FitnessAgent.get_recommended_models())
|
| 123 |
return f"""β **Model Not Available**
|
| 124 |
|
| 125 |
-
π« **Error:** Model `{new_model}` is not currently available on your
|
| 126 |
|
| 127 |
π‘ **This could mean:**
|
| 128 |
- The model requires special access or higher tier subscription
|
|
@@ -133,14 +148,34 @@ def change_model(new_model: str) -> str:
|
|
| 133 |
{recommended}
|
| 134 |
|
| 135 |
π§ **Current Model:** `{current_model}` (unchanged)"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
else:
|
| 137 |
return f"""β **Model Error**
|
| 138 |
|
| 139 |
π« **Error:** Failed to initialize model `{new_model}`
|
| 140 |
|
| 141 |
-
π **
|
|
|
|
| 142 |
|
| 143 |
-
π§ **Current Model:** `{current_model}` (unchanged)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
# Reset agent to force recreation with new model
|
| 146 |
current_agent = None
|
|
@@ -163,6 +198,83 @@ def change_model(new_model: str) -> str:
|
|
| 163 |
return f"β **Unexpected Error:** {str(e)}"
|
| 164 |
|
| 165 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
def update_model_and_display(selected_model: str) -> str:
|
| 167 |
"""
|
| 168 |
Update both the model and the display when dropdown selection changes
|
|
@@ -174,10 +286,16 @@ def update_model_and_display(selected_model: str) -> str:
|
|
| 174 |
Formatted model information
|
| 175 |
"""
|
| 176 |
# Ignore separator selections
|
| 177 |
-
|
| 178 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
|
| 180 |
-
The separator "
|
| 181 |
|
| 182 |
Please choose one of the actual model names from the dropdown."""
|
| 183 |
|
|
@@ -188,7 +306,11 @@ Please choose one of the actual model names from the dropdown."""
|
|
| 188 |
if "β
" in change_result:
|
| 189 |
try:
|
| 190 |
model_info = FitnessAgent.get_model_info(selected_model)
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
π‘ **Description:** {model_info}
|
| 194 |
|
|
@@ -944,27 +1066,41 @@ with gr.Blocks(
|
|
| 944 |
|
| 945 |
# Model selection section
|
| 946 |
with gr.Row():
|
| 947 |
-
with gr.Column(
|
| 948 |
-
|
| 949 |
-
|
| 950 |
-
recommended_models = FitnessAgent.get_recommended_models()
|
| 951 |
|
| 952 |
-
# Create
|
| 953 |
-
|
| 954 |
-
model_choices = recommended_models + ["--- Legacy/Experimental ---"] + other_models
|
| 955 |
|
| 956 |
-
|
| 957 |
-
|
| 958 |
-
|
| 959 |
-
|
| 960 |
-
|
| 961 |
-
|
| 962 |
-
elem_classes=["model-
|
| 963 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 964 |
|
| 965 |
# Model information display
|
| 966 |
model_info_display = gr.Markdown(
|
| 967 |
-
value=f"""
|
| 968 |
|
| 969 |
π‘ **Description:** {FitnessAgent.get_model_info('claude-3.5-haiku')}
|
| 970 |
|
|
@@ -1032,11 +1168,14 @@ with gr.Blocks(
|
|
| 1032 |
- Let me know about any injuries or limitations
|
| 1033 |
|
| 1034 |
**AI Model Selection:**
|
| 1035 |
-
-
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
-
|
|
|
|
|
|
|
| 1039 |
- You can change models anytime - the conversation continues seamlessly
|
|
|
|
| 1040 |
|
| 1041 |
**Conversation Management:**
|
| 1042 |
- The assistant remembers our entire conversation
|
|
@@ -1054,24 +1193,40 @@ with gr.Blocks(
|
|
| 1054 |
|
| 1055 |
# Add model comparison section
|
| 1056 |
with gr.Accordion("π€ Model Comparison Guide", open=False):
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
| 1067 |
-
|
| 1068 |
-
|
| 1069 |
-
|
| 1070 |
-
|
| 1071 |
-
|
| 1072 |
-
|
| 1073 |
-
|
| 1074 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1075 |
|
| 1076 |
# Event handlers
|
| 1077 |
chat_msg = chat_input.submit(
|
|
@@ -1079,17 +1234,24 @@ with gr.Blocks(
|
|
| 1079 |
)
|
| 1080 |
bot_msg = chat_msg.then(
|
| 1081 |
dynamic_bot,
|
| 1082 |
-
[chatbot, streaming_toggle,
|
| 1083 |
chatbot,
|
| 1084 |
api_name="bot_response"
|
| 1085 |
)
|
| 1086 |
bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
|
| 1087 |
|
| 1088 |
-
#
|
| 1089 |
-
|
| 1090 |
-
|
| 1091 |
-
inputs=[
|
| 1092 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1093 |
)
|
| 1094 |
|
| 1095 |
# Clear conversation handler
|
|
|
|
| 113 |
|
| 114 |
# Test if we can create an agent with this model (basic validation)
|
| 115 |
try:
|
| 116 |
+
logger.info(f"Attempting to create test agent with model: {new_model}")
|
| 117 |
test_agent = FitnessAgent(new_model)
|
| 118 |
+
logger.info(f"Successfully created test agent with model: {new_model}")
|
| 119 |
# If we get here, the model is likely available
|
| 120 |
except Exception as model_error:
|
| 121 |
+
# Get the full error details for debugging
|
| 122 |
error_str = str(model_error)
|
| 123 |
+
error_repr = repr(model_error)
|
| 124 |
+
error_type = type(model_error).__name__
|
| 125 |
+
|
| 126 |
+
logger.error(f"Model initialization error for {new_model}:")
|
| 127 |
+
logger.error(f" Error type: {error_type}")
|
| 128 |
+
logger.error(f" Error string: {error_str}")
|
| 129 |
+
logger.error(f" Error repr: {error_repr}")
|
| 130 |
+
|
| 131 |
+
# Clean up error message for display
|
| 132 |
+
clean_error = error_str.replace('\n', ' ').replace('\r', ' ')
|
| 133 |
+
clean_error = clean_error[:400] + "..." if len(clean_error) > 400 else clean_error
|
| 134 |
+
|
| 135 |
+
# Check for specific error types
|
| 136 |
+
if "not_found_error" in error_str.lower() or "notfounderror" in error_str.lower():
|
| 137 |
recommended = ", ".join(FitnessAgent.get_recommended_models())
|
| 138 |
return f"""β **Model Not Available**
|
| 139 |
|
| 140 |
+
π« **Error:** Model `{new_model}` is not currently available on your API account.
|
| 141 |
|
| 142 |
π‘ **This could mean:**
|
| 143 |
- The model requires special access or higher tier subscription
|
|
|
|
| 148 |
{recommended}
|
| 149 |
|
| 150 |
π§ **Current Model:** `{current_model}` (unchanged)"""
|
| 151 |
+
elif "api" in error_str.lower() and "key" in error_str.lower():
|
| 152 |
+
return f"""β **API Configuration Error**
|
| 153 |
+
|
| 154 |
+
π« **Error:** There seems to be an issue with your Anthropic API configuration.
|
| 155 |
+
|
| 156 |
+
π‘ **Please check:**
|
| 157 |
+
- Your ANTHROPIC_API_KEY environment variable is set
|
| 158 |
+
- Your API key is valid and has the necessary permissions
|
| 159 |
+
- Your account has access to the requested model
|
| 160 |
+
|
| 161 |
+
π **Error Details:** {clean_error}
|
| 162 |
+
|
| 163 |
+
π§ **Current Model:** `{current_model}` (unchanged)
|
| 164 |
+
|
| 165 |
+
π‘ **Tip:** Try using an OpenAI model if Anthropic models are not working."""
|
| 166 |
else:
|
| 167 |
return f"""β **Model Error**
|
| 168 |
|
| 169 |
π« **Error:** Failed to initialize model `{new_model}`
|
| 170 |
|
| 171 |
+
π **Error Type:** {error_type}
|
| 172 |
+
π **Details:** {clean_error}
|
| 173 |
|
| 174 |
+
π§ **Current Model:** `{current_model}` (unchanged)
|
| 175 |
+
|
| 176 |
+
π‘ **Debugging Info:**
|
| 177 |
+
- Provider: {"Anthropic" if "claude" in new_model else "OpenAI" if any(x in new_model for x in ["gpt", "o1", "o3"]) else "Unknown"}
|
| 178 |
+
- Try using a different model or check your API configuration"""
|
| 179 |
|
| 180 |
# Reset agent to force recreation with new model
|
| 181 |
current_agent = None
|
|
|
|
| 198 |
return f"β **Unexpected Error:** {str(e)}"
|
| 199 |
|
| 200 |
|
| 201 |
+
def filter_model_table(filter_choice: str) -> list:
|
| 202 |
+
"""Filter the model table based on user selection."""
|
| 203 |
+
all_data = FitnessAgent.get_models_table_data()
|
| 204 |
+
|
| 205 |
+
if filter_choice == "π΅ Anthropic Only":
|
| 206 |
+
return [row for row in all_data if "π΅ Anthropic" in row[1]]
|
| 207 |
+
elif filter_choice == "π’ OpenAI Only":
|
| 208 |
+
return [row for row in all_data if "π’ OpenAI" in row[1]]
|
| 209 |
+
elif filter_choice == "β Recommended Only":
|
| 210 |
+
return [row for row in all_data if row[0] == "β"]
|
| 211 |
+
else: # All Models
|
| 212 |
+
return all_data
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def select_model_from_table(table_data, evt: gr.SelectData):
|
| 216 |
+
"""Select a model from the table"""
|
| 217 |
+
try:
|
| 218 |
+
if evt is None:
|
| 219 |
+
return "", "Please select a model from the table"
|
| 220 |
+
|
| 221 |
+
# Get the selected row index
|
| 222 |
+
row_index = evt.index[0] if evt.index else 0
|
| 223 |
+
|
| 224 |
+
# Handle both DataFrame and list formats
|
| 225 |
+
try:
|
| 226 |
+
# Try pandas DataFrame access first
|
| 227 |
+
if hasattr(table_data, 'iloc') and row_index < len(table_data):
|
| 228 |
+
row = table_data.iloc[row_index]
|
| 229 |
+
if len(row) >= 7:
|
| 230 |
+
rating = row.iloc[0] # Recommendation star
|
| 231 |
+
provider = row.iloc[1] # Provider
|
| 232 |
+
selected_model = row.iloc[2] # Model name
|
| 233 |
+
capability = row.iloc[3] # Capability rating
|
| 234 |
+
speed = row.iloc[4] # Speed rating
|
| 235 |
+
cost = row.iloc[5] # Cost rating
|
| 236 |
+
description = row.iloc[6] # Description
|
| 237 |
+
else:
|
| 238 |
+
return "", "Invalid table row - insufficient columns"
|
| 239 |
+
# Fall back to list access
|
| 240 |
+
elif isinstance(table_data, list) and row_index < len(table_data) and len(table_data[row_index]) >= 7:
|
| 241 |
+
rating = table_data[row_index][0] # Recommendation star
|
| 242 |
+
provider = table_data[row_index][1] # Provider
|
| 243 |
+
selected_model = table_data[row_index][2] # Model name
|
| 244 |
+
capability = table_data[row_index][3] # Capability rating
|
| 245 |
+
speed = table_data[row_index][4] # Speed rating
|
| 246 |
+
cost = table_data[row_index][5] # Cost rating
|
| 247 |
+
description = table_data[row_index][6] # Description
|
| 248 |
+
else:
|
| 249 |
+
return "", "Invalid selection - please try clicking on a model row"
|
| 250 |
+
|
| 251 |
+
except (IndexError, KeyError) as data_error:
|
| 252 |
+
logger.error(f"Data access error: {str(data_error)} - Table type: {type(table_data)}, Row index: {row_index}")
|
| 253 |
+
return "", "Error accessing table data - please try again"
|
| 254 |
+
|
| 255 |
+
# Update the model and get the change result
|
| 256 |
+
change_result = change_model(selected_model)
|
| 257 |
+
|
| 258 |
+
if "β
" in change_result:
|
| 259 |
+
model_info = f"""β
**Model Successfully Selected!**
|
| 260 |
+
|
| 261 |
+
π€ **Current Model:** `{selected_model}`
|
| 262 |
+
{provider}
|
| 263 |
+
**Capability:** {capability} | **Speed:** {speed} | **Cost:** {cost}
|
| 264 |
+
|
| 265 |
+
π‘ **Description:** {description}
|
| 266 |
+
|
| 267 |
+
π **Status:** Ready to chat with the new model!"""
|
| 268 |
+
else:
|
| 269 |
+
model_info = change_result # Show the error message
|
| 270 |
+
|
| 271 |
+
return selected_model, model_info
|
| 272 |
+
|
| 273 |
+
except Exception as e:
|
| 274 |
+
logger.error(f"Error in select_model_from_table: {str(e)} - Table type: {type(table_data)}, Row index: {row_index if 'row_index' in locals() else 'unknown'}")
|
| 275 |
+
return "", f"Error selecting model: {str(e)}"
|
| 276 |
+
|
| 277 |
+
|
| 278 |
def update_model_and_display(selected_model: str) -> str:
|
| 279 |
"""
|
| 280 |
Update both the model and the display when dropdown selection changes
|
|
|
|
| 286 |
Formatted model information
|
| 287 |
"""
|
| 288 |
# Ignore separator selections
|
| 289 |
+
separators = [
|
| 290 |
+
"--- Anthropic Models ---",
|
| 291 |
+
"--- OpenAI Models ---",
|
| 292 |
+
"--- Other Models ---",
|
| 293 |
+
"--- Legacy/Experimental ---"
|
| 294 |
+
]
|
| 295 |
+
if selected_model in separators:
|
| 296 |
+
return f"""β οΈ **Please select a specific model**
|
| 297 |
|
| 298 |
+
The separator "{selected_model}" is not a valid model choice.
|
| 299 |
|
| 300 |
Please choose one of the actual model names from the dropdown."""
|
| 301 |
|
|
|
|
| 306 |
if "β
" in change_result:
|
| 307 |
try:
|
| 308 |
model_info = FitnessAgent.get_model_info(selected_model)
|
| 309 |
+
|
| 310 |
+
# Determine provider emoji
|
| 311 |
+
provider_emoji = "π΅" if "claude" in selected_model else "π’" if any(x in selected_model for x in ["gpt", "o1", "o3"]) else "βͺ"
|
| 312 |
+
|
| 313 |
+
return f"""{provider_emoji} **Current Model:** `{selected_model}`
|
| 314 |
|
| 315 |
π‘ **Description:** {model_info}
|
| 316 |
|
|
|
|
| 1066 |
|
| 1067 |
# Model selection section
|
| 1068 |
with gr.Row():
|
| 1069 |
+
with gr.Column():
|
| 1070 |
+
gr.Markdown("### π€ AI Model Selection")
|
| 1071 |
+
gr.Markdown("Browse and select your preferred AI model. Click on a row to select it.")
|
|
|
|
| 1072 |
|
| 1073 |
+
# Create model table data
|
| 1074 |
+
table_data = FitnessAgent.get_models_table_data()
|
|
|
|
| 1075 |
|
| 1076 |
+
model_table = gr.DataFrame(
|
| 1077 |
+
value=table_data,
|
| 1078 |
+
headers=["β", "Provider", "Model Name", "Capability", "Speed", "Cost", "Description"],
|
| 1079 |
+
datatype=["str", "str", "str", "str", "str", "str", "str"],
|
| 1080 |
+
interactive=False,
|
| 1081 |
+
wrap=True,
|
| 1082 |
+
elem_classes=["model-table"]
|
| 1083 |
)
|
| 1084 |
+
|
| 1085 |
+
# Hidden components to manage selection
|
| 1086 |
+
selected_model = gr.Textbox(
|
| 1087 |
+
value="claude-3.5-haiku",
|
| 1088 |
+
visible=False,
|
| 1089 |
+
label="Selected Model"
|
| 1090 |
+
)
|
| 1091 |
+
|
| 1092 |
+
# Model selection button
|
| 1093 |
+
with gr.Row():
|
| 1094 |
+
model_filter = gr.Dropdown(
|
| 1095 |
+
choices=["All Models", "π΅ Anthropic Only", "π’ OpenAI Only", "β Recommended Only"],
|
| 1096 |
+
value="All Models",
|
| 1097 |
+
label="Filter Models",
|
| 1098 |
+
scale=3
|
| 1099 |
+
)
|
| 1100 |
|
| 1101 |
# Model information display
|
| 1102 |
model_info_display = gr.Markdown(
|
| 1103 |
+
value=f"""π΅ **Current Model:** `claude-3.5-haiku`
|
| 1104 |
|
| 1105 |
π‘ **Description:** {FitnessAgent.get_model_info('claude-3.5-haiku')}
|
| 1106 |
|
|
|
|
| 1168 |
- Let me know about any injuries or limitations
|
| 1169 |
|
| 1170 |
**AI Model Selection:**
|
| 1171 |
+
- **π΅ Anthropic Claude Models**: Excellent for detailed reasoning and analysis
|
| 1172 |
+
- Claude-4: Most capable (premium), Claude-3.7: Extended thinking
|
| 1173 |
+
- Claude-3.5: Balanced performance, Claude-3: Fast and cost-effective
|
| 1174 |
+
- **π’ OpenAI GPT Models**: Great for general tasks and familiar interface
|
| 1175 |
+
- GPT-4o: Latest with vision, GPT-4 Turbo: Large context window
|
| 1176 |
+
- GPT-3.5: Fast and economical, o1/o3: Advanced reasoning
|
| 1177 |
- You can change models anytime - the conversation continues seamlessly
|
| 1178 |
+
- Mix and match providers based on your preferences
|
| 1179 |
|
| 1180 |
**Conversation Management:**
|
| 1181 |
- The assistant remembers our entire conversation
|
|
|
|
| 1193 |
|
| 1194 |
# Add model comparison section
|
| 1195 |
with gr.Accordion("π€ Model Comparison Guide", open=False):
|
| 1196 |
+
gr.Markdown("""
|
| 1197 |
+
## π΅ Anthropic Claude Models
|
| 1198 |
+
|
| 1199 |
+
| Model | Capability | Speed | Cost | Best For |
|
| 1200 |
+
|-------|------------|--------|------|----------|
|
| 1201 |
+
| claude-4-opus | β
β
β
β
β
| β
β
β
ββ | β
β
β
β
β
| Complex analysis, detailed plans |
|
| 1202 |
+
| claude-4-sonnet | β
β
β
β
β | β
β
β
β
β | β
β
β
β
β | Balanced high performance |
|
| 1203 |
+
| claude-3.7-sonnet | β
β
β
β
β | β
β
β
β
β | β
β
β
ββ | Extended thinking, complex tasks |
|
| 1204 |
+
| claude-3.5-sonnet | β
β
β
β
β | β
β
β
β
β | β
β
β
ββ | General use, balanced |
|
| 1205 |
+
| claude-3.5-haiku | β
β
β
ββ | β
β
β
β
β
| β
β
βββ | **DEFAULT** - Fast responses |
|
| 1206 |
+
| claude-3-haiku | β
β
β
ββ | β
β
β
β
β
| β
ββββ | Most cost-effective |
|
| 1207 |
+
|
| 1208 |
+
## π’ OpenAI GPT Models
|
| 1209 |
+
|
| 1210 |
+
| Model | Capability | Speed | Cost | Best For |
|
| 1211 |
+
|-------|------------|--------|------|----------|
|
| 1212 |
+
| gpt-4o | β
β
β
β
β
| β
β
β
β
β | β
β
β
β
β | Latest features, vision support |
|
| 1213 |
+
| gpt-4o-mini | β
β
β
β
β | β
β
β
β
β
| β
β
βββ | Balanced performance, affordable |
|
| 1214 |
+
| gpt-4-turbo | β
β
β
β
β | β
β
β
β
β | β
β
β
β
β | Large context, reliable |
|
| 1215 |
+
| gpt-3.5-turbo | β
β
β
ββ | β
β
β
β
β
| β
ββββ | Fast and economical |
|
| 1216 |
+
| o1-preview | β
β
β
β
β
| β
β
βββ | β
β
β
β
β
| Advanced reasoning |
|
| 1217 |
+
| o1-mini | β
β
β
β
β | β
β
β
ββ | β
β
β
ββ | Reasoning tasks |
|
| 1218 |
+
| o3-mini | β
β
β
β
β | β
β
β
β
β | β
β
β
ββ | Latest reasoning model |
|
| 1219 |
+
|
| 1220 |
+
### π‘ Provider Comparison
|
| 1221 |
+
- **π΅ Anthropic**: Excellent for detailed analysis, safety-focused, great for complex fitness planning
|
| 1222 |
+
- **π’ OpenAI**: Familiar interface, good general performance, strong tool usage
|
| 1223 |
+
|
| 1224 |
+
### π― Recommendations by Use Case
|
| 1225 |
+
- **Quick questions**: claude-3.5-haiku, gpt-4o-mini, gpt-3.5-turbo
|
| 1226 |
+
- **Comprehensive plans**: claude-3.5-sonnet, gpt-4o, claude-3.7-sonnet
|
| 1227 |
+
- **Complex analysis**: claude-4-opus, gpt-4o, o1-preview
|
| 1228 |
+
- **Budget-conscious**: claude-3-haiku, gpt-3.5-turbo, gpt-4o-mini
|
| 1229 |
+
""")
|
| 1230 |
|
| 1231 |
# Event handlers
|
| 1232 |
chat_msg = chat_input.submit(
|
|
|
|
| 1234 |
)
|
| 1235 |
bot_msg = chat_msg.then(
|
| 1236 |
dynamic_bot,
|
| 1237 |
+
[chatbot, streaming_toggle, selected_model],
|
| 1238 |
chatbot,
|
| 1239 |
api_name="bot_response"
|
| 1240 |
)
|
| 1241 |
bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
|
| 1242 |
|
| 1243 |
+
# Model table filtering
|
| 1244 |
+
model_filter.change(
|
| 1245 |
+
filter_model_table,
|
| 1246 |
+
inputs=[model_filter],
|
| 1247 |
+
outputs=[model_table]
|
| 1248 |
+
)
|
| 1249 |
+
|
| 1250 |
+
# Model selection from table
|
| 1251 |
+
model_table.select(
|
| 1252 |
+
select_model_from_table,
|
| 1253 |
+
inputs=[model_table],
|
| 1254 |
+
outputs=[selected_model, model_info_display]
|
| 1255 |
)
|
| 1256 |
|
| 1257 |
# Clear conversation handler
|
fitness_agent/examples.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
"""
|
| 2 |
-
Example usage of the Fitness Agent with different
|
| 3 |
|
| 4 |
-
Run this script to see different ways to use the FitnessAgent
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
from fitness_agent import FitnessAgent
|
|
@@ -13,59 +14,133 @@ def basic_example():
|
|
| 13 |
print("=== Basic Usage (Default Model) ===")
|
| 14 |
agent = FitnessAgent()
|
| 15 |
print(f"Using model: {agent.model_name}")
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# In a real scenario, you would run this:
|
| 18 |
# result = Runner.run_sync(agent, "Create a beginner fitness plan.")
|
| 19 |
# print(f"Result: {result.final_output}")
|
| 20 |
-
print("Agent created successfully!")
|
| 21 |
print()
|
| 22 |
|
| 23 |
-
def
|
| 24 |
-
"""Example using
|
| 25 |
-
print("===
|
| 26 |
agent = FitnessAgent("claude-3.5-sonnet")
|
| 27 |
print(f"Using model: {agent.model_name}")
|
| 28 |
-
print("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
print()
|
| 30 |
|
| 31 |
def environment_variable_example():
|
| 32 |
"""Example using environment variable."""
|
| 33 |
print("=== Using Environment Variable ===")
|
| 34 |
# Set environment variable (in practice, this would be in your .env file)
|
| 35 |
-
os.environ["
|
| 36 |
|
| 37 |
agent = FitnessAgent() # Will use the model from environment variable
|
| 38 |
print(f"Using model: {agent.model_name}")
|
| 39 |
-
print("
|
|
|
|
| 40 |
print()
|
| 41 |
|
| 42 |
def list_available_models():
|
| 43 |
-
"""Display all available models."""
|
| 44 |
-
print("=== Available Models ===")
|
| 45 |
-
models = FitnessAgent.list_supported_models()
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
info = FitnessAgent.get_model_info(model)
|
| 51 |
-
print(f" β’ {model}
|
| 52 |
-
print(f" {
|
|
|
|
| 53 |
print()
|
| 54 |
|
| 55 |
-
print("
|
| 56 |
-
for
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
print()
|
| 59 |
|
| 60 |
if __name__ == "__main__":
|
| 61 |
-
print("π€ Fitness Agent Examples")
|
| 62 |
-
print("=" *
|
| 63 |
|
| 64 |
list_available_models()
|
|
|
|
| 65 |
basic_example()
|
| 66 |
-
|
|
|
|
|
|
|
| 67 |
environment_variable_example()
|
| 68 |
|
|
|
|
| 69 |
print("β
All examples completed!")
|
| 70 |
-
print(
|
| 71 |
-
print("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
Example usage of the Fitness Agent with different AI providers and models.
|
| 3 |
|
| 4 |
+
Run this script to see different ways to use the FitnessAgent with both
|
| 5 |
+
Anthropic Claude and OpenAI GPT models.
|
| 6 |
"""
|
| 7 |
|
| 8 |
from fitness_agent import FitnessAgent
|
|
|
|
| 14 |
print("=== Basic Usage (Default Model) ===")
|
| 15 |
agent = FitnessAgent()
|
| 16 |
print(f"Using model: {agent.model_name}")
|
| 17 |
+
print(f"Provider: {agent.provider}")
|
| 18 |
+
print(f"Final model path: {agent.final_model}")
|
| 19 |
|
| 20 |
# In a real scenario, you would run this:
|
| 21 |
# result = Runner.run_sync(agent, "Create a beginner fitness plan.")
|
| 22 |
# print(f"Result: {result.final_output}")
|
| 23 |
+
print("β
Agent created successfully!")
|
| 24 |
print()
|
| 25 |
|
| 26 |
+
def anthropic_example():
|
| 27 |
+
"""Example using Anthropic Claude models."""
|
| 28 |
+
print("=== Anthropic Claude Example ===")
|
| 29 |
agent = FitnessAgent("claude-3.5-sonnet")
|
| 30 |
print(f"Using model: {agent.model_name}")
|
| 31 |
+
print(f"Provider: {agent.provider}")
|
| 32 |
+
print(f"Final model path: {agent.final_model}")
|
| 33 |
+
print("β
Anthropic agent created successfully!")
|
| 34 |
+
print()
|
| 35 |
+
|
| 36 |
+
def openai_example():
|
| 37 |
+
"""Example using OpenAI GPT models."""
|
| 38 |
+
print("=== OpenAI GPT Example ===")
|
| 39 |
+
try:
|
| 40 |
+
agent = FitnessAgent("gpt-4o-mini")
|
| 41 |
+
print(f"Using model: {agent.model_name}")
|
| 42 |
+
print(f"Provider: {agent.provider}")
|
| 43 |
+
print(f"Final model path: {agent.final_model}")
|
| 44 |
+
print("β
OpenAI agent created successfully!")
|
| 45 |
+
except Exception as e:
|
| 46 |
+
print(f"β οΈ Could not create OpenAI agent: {e}")
|
| 47 |
+
print(" (Make sure you have OPENAI_API_KEY set in your .env file)")
|
| 48 |
+
print()
|
| 49 |
+
|
| 50 |
+
def reasoning_model_example():
|
| 51 |
+
"""Example using OpenAI reasoning models (o1/o3 series)."""
|
| 52 |
+
print("=== OpenAI Reasoning Model Example ===")
|
| 53 |
+
try:
|
| 54 |
+
agent = FitnessAgent("o1-mini")
|
| 55 |
+
print(f"Using model: {agent.model_name}")
|
| 56 |
+
print(f"Provider: {agent.provider}")
|
| 57 |
+
print(f"Final model path: {agent.final_model}")
|
| 58 |
+
print("β
Reasoning model agent created successfully!")
|
| 59 |
+
print("π‘ o1-mini is great for complex fitness planning and analysis")
|
| 60 |
+
except Exception as e:
|
| 61 |
+
print(f"β οΈ Could not create reasoning model agent: {e}")
|
| 62 |
+
print(" (o1 models may require special access)")
|
| 63 |
print()
|
| 64 |
|
| 65 |
def environment_variable_example():
|
| 66 |
"""Example using environment variable."""
|
| 67 |
print("=== Using Environment Variable ===")
|
| 68 |
# Set environment variable (in practice, this would be in your .env file)
|
| 69 |
+
os.environ["AI_MODEL"] = "gpt-4o"
|
| 70 |
|
| 71 |
agent = FitnessAgent() # Will use the model from environment variable
|
| 72 |
print(f"Using model: {agent.model_name}")
|
| 73 |
+
print(f"Provider: {agent.provider}")
|
| 74 |
+
print("β
Agent created with environment variable!")
|
| 75 |
print()
|
| 76 |
|
| 77 |
def list_available_models():
|
| 78 |
+
"""Display all available models organized by provider."""
|
| 79 |
+
print("=== Available Models by Provider ===")
|
|
|
|
| 80 |
|
| 81 |
+
providers = FitnessAgent.get_models_by_provider()
|
| 82 |
+
|
| 83 |
+
print("π΅ ANTHROPIC MODELS:")
|
| 84 |
+
for model in providers["anthropic"]:
|
| 85 |
+
full_name = providers["anthropic"][model]
|
| 86 |
+
info = FitnessAgent.get_model_info(model)
|
| 87 |
+
print(f" β’ {model}")
|
| 88 |
+
print(f" Path: {full_name}")
|
| 89 |
+
print(f" Info: {info}")
|
| 90 |
+
print()
|
| 91 |
+
|
| 92 |
+
print("π’ OPENAI MODELS:")
|
| 93 |
+
for model in providers["openai"]:
|
| 94 |
+
full_name = providers["openai"][model]
|
| 95 |
info = FitnessAgent.get_model_info(model)
|
| 96 |
+
print(f" β’ {model}")
|
| 97 |
+
print(f" Path: {full_name}")
|
| 98 |
+
print(f" Info: {info}")
|
| 99 |
print()
|
| 100 |
|
| 101 |
+
print("π― RECOMMENDED MODELS:")
|
| 102 |
+
for model in FitnessAgent.get_recommended_models():
|
| 103 |
+
provider_icon = "π΅" if "claude" in model else "π’" if any(x in model for x in ["gpt", "o1", "o3"]) else "βͺ"
|
| 104 |
+
print(f" {provider_icon} {model}")
|
| 105 |
+
print()
|
| 106 |
+
|
| 107 |
+
def compare_providers():
|
| 108 |
+
"""Show comparison between providers."""
|
| 109 |
+
print("=== Provider Comparison ===")
|
| 110 |
+
print("π΅ ANTHROPIC CLAUDE:")
|
| 111 |
+
print(" β
Excellent for detailed analysis and safety")
|
| 112 |
+
print(" β
Great context understanding")
|
| 113 |
+
print(" β
Strong reasoning capabilities")
|
| 114 |
+
print(" β Requires ANTHROPIC_API_KEY")
|
| 115 |
+
print()
|
| 116 |
+
|
| 117 |
+
print("π’ OPENAI GPT:")
|
| 118 |
+
print(" β
Familiar and widely used")
|
| 119 |
+
print(" β
Good general performance")
|
| 120 |
+
print(" β
Vision capabilities (GPT-4o)")
|
| 121 |
+
print(" β
Reasoning models (o1/o3 series)")
|
| 122 |
+
print(" β Requires OPENAI_API_KEY")
|
| 123 |
print()
|
| 124 |
|
| 125 |
if __name__ == "__main__":
|
| 126 |
+
print("π€ Fitness Agent Examples - Multi-Provider Support")
|
| 127 |
+
print("=" * 60)
|
| 128 |
|
| 129 |
list_available_models()
|
| 130 |
+
compare_providers()
|
| 131 |
basic_example()
|
| 132 |
+
anthropic_example()
|
| 133 |
+
openai_example()
|
| 134 |
+
reasoning_model_example()
|
| 135 |
environment_variable_example()
|
| 136 |
|
| 137 |
+
print("=" * 60)
|
| 138 |
print("β
All examples completed!")
|
| 139 |
+
print()
|
| 140 |
+
print("π‘ To actually run the agents:")
|
| 141 |
+
print(" 1. Copy .env.example to .env")
|
| 142 |
+
print(" 2. Add your OPENAI_API_KEY and/or ANTHROPIC_API_KEY")
|
| 143 |
+
print(" 3. Uncomment the Runner.run_sync lines in the examples")
|
| 144 |
+
print(" 4. Run: python fitness_agent/examples.py")
|
| 145 |
+
print()
|
| 146 |
+
print("π Or launch the web interface: python fitness_agent/app.py")
|
fitness_agent/fitness_agent.py
CHANGED
|
@@ -17,49 +17,78 @@ class FitnessAgent(Agent):
|
|
| 17 |
"""
|
| 18 |
A helpful assistant for general fitness guidance and handoffs to a plan-building agent.
|
| 19 |
|
| 20 |
-
Supports
|
|
|
|
|
|
|
| 21 |
- Claude-4: claude-opus-4-20250514, claude-sonnet-4-20250514 (Premium)
|
| 22 |
- Claude-3.7: claude-3-7-sonnet-20250219 (Extended thinking)
|
| 23 |
- Claude-3.5: claude-3-5-sonnet-20241022 (latest), claude-3-5-sonnet-20240620 (stable), claude-3-5-haiku-20241022 (fast)
|
| 24 |
- Claude-3: claude-3-haiku-20240307 (legacy but reliable)
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
"""
|
| 29 |
|
| 30 |
-
# Available
|
| 31 |
-
# Updated to
|
| 32 |
SUPPORTED_MODELS = {
|
|
|
|
| 33 |
# Claude-4 models (latest generation - may require special access)
|
| 34 |
-
"claude-4-opus": "claude-opus-4-20250514",
|
| 35 |
-
"claude-4-sonnet": "claude-sonnet-4-20250514",
|
| 36 |
|
| 37 |
# Claude-3.7 models (newest stable)
|
| 38 |
-
"claude-3.7-sonnet": "claude-3-7-sonnet-20250219",
|
| 39 |
|
| 40 |
# Claude-3.5 models (widely available)
|
| 41 |
-
"claude-3.5-sonnet-latest": "claude-3-5-sonnet-20241022", # Latest version
|
| 42 |
-
"claude-3.5-sonnet": "claude-3-5-sonnet-20240620", # Previous stable version
|
| 43 |
-
"claude-3.5-haiku": "claude-3-5-haiku-20241022", # New Haiku 3.5 model
|
| 44 |
|
| 45 |
# Claude-3 models (legacy but still available)
|
| 46 |
-
"claude-3-haiku": "claude-3-haiku-20240307",
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
}
|
| 49 |
|
| 50 |
def __init__(self, model_name: Optional[str] = None):
|
| 51 |
"""
|
| 52 |
-
Initialize the Fitness Agent with configurable Anthropic
|
| 53 |
|
| 54 |
Args:
|
| 55 |
-
model_name: Name of the
|
| 56 |
-
or a full model identifier. Defaults to claude-3-haiku if not specified.
|
| 57 |
-
Can also be set via ANTHROPIC_MODEL environment
|
| 58 |
"""
|
| 59 |
# Determine which model to use
|
| 60 |
if model_name is None:
|
| 61 |
-
# Check environment
|
| 62 |
-
model_name =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
|
| 64 |
# Validate the model name
|
| 65 |
is_valid, validation_message = self.validate_model_name(model_name)
|
|
@@ -75,27 +104,30 @@ class FitnessAgent(Agent):
|
|
| 75 |
# Assume it's already a full model identifier
|
| 76 |
full_model_name = model_name
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
if
|
| 80 |
-
|
|
|
|
| 81 |
else:
|
| 82 |
-
|
|
|
|
| 83 |
|
| 84 |
# Store the model information for debugging
|
| 85 |
self.model_name = model_name
|
| 86 |
self.full_model_name = full_model_name
|
| 87 |
-
self.
|
|
|
|
| 88 |
|
| 89 |
fitness_plan_agent = Agent(
|
| 90 |
name="Fitness Plan Assistant",
|
| 91 |
instructions="You are a helpful assistant for creating personalized fitness plans.",
|
| 92 |
-
model=
|
| 93 |
output_type=FitnessPlan
|
| 94 |
)
|
| 95 |
|
| 96 |
super().__init__(
|
| 97 |
name="Fitness Assistant",
|
| 98 |
-
model=
|
| 99 |
instructions="""
|
| 100 |
You are a helpful assistant for fitness-related queries.
|
| 101 |
|
|
@@ -104,6 +136,30 @@ class FitnessAgent(Agent):
|
|
| 104 |
handoffs=[fitness_plan_agent]
|
| 105 |
)
|
| 106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
@classmethod
|
| 108 |
def list_supported_models(cls) -> dict:
|
| 109 |
"""Return a dictionary of supported model names and their full identifiers."""
|
|
@@ -113,13 +169,24 @@ class FitnessAgent(Agent):
|
|
| 113 |
def get_model_info(cls, model_name: str) -> str:
|
| 114 |
"""Get information about a specific model."""
|
| 115 |
model_info = {
|
|
|
|
| 116 |
"claude-4-opus": "Most capable and intelligent model. Superior reasoning, complex tasks (Premium tier)",
|
| 117 |
-
"claude-4-sonnet": "High-performance model with exceptional reasoning and efficiency (Premium tier)",
|
| 118 |
"claude-3.7-sonnet": "Enhanced model with extended thinking capabilities (Recommended)",
|
| 119 |
"claude-3.5-sonnet-latest": "Latest Claude 3.5 Sonnet with improved capabilities (Recommended)",
|
| 120 |
"claude-3.5-sonnet": "Excellent balance of intelligence and speed (Stable version)",
|
| 121 |
"claude-3.5-haiku": "Fast and compact model for near-instant responsiveness (New!)",
|
| 122 |
"claude-3-haiku": "Fastest model, good for simple tasks and cost-effective (Legacy but reliable)",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
}
|
| 124 |
return model_info.get(model_name, "Model information not available")
|
| 125 |
|
|
@@ -127,13 +194,128 @@ class FitnessAgent(Agent):
|
|
| 127 |
def get_recommended_models(cls) -> list:
|
| 128 |
"""Get a list of recommended models that are most likely to be available."""
|
| 129 |
return [
|
| 130 |
-
|
| 131 |
-
"claude-3.5-haiku", # New fast model
|
|
|
|
| 132 |
"claude-3.5-sonnet", # Stable version, widely available
|
| 133 |
"claude-3.5-sonnet-latest", # Latest improvements
|
| 134 |
"claude-3.7-sonnet", # Newest stable with extended thinking
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
]
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
@classmethod
|
| 138 |
def validate_model_name(cls, model_name: str) -> tuple[bool, str]:
|
| 139 |
"""
|
|
@@ -154,20 +336,59 @@ class FitnessAgent(Agent):
|
|
| 154 |
|
| 155 |
if __name__ == "__main__":
|
| 156 |
# Example usage with different models
|
| 157 |
-
print("Available Anthropic
|
| 158 |
-
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
print(f" {FitnessAgent.get_model_info(name)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
|
| 162 |
-
print("\n" + "="*
|
| 163 |
|
| 164 |
-
# Create agent with
|
| 165 |
-
print("Creating agent with
|
| 166 |
agent = FitnessAgent()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
|
|
|
| 171 |
|
| 172 |
-
|
| 173 |
-
|
|
|
|
|
|
| 17 |
"""
|
| 18 |
A helpful assistant for general fitness guidance and handoffs to a plan-building agent.
|
| 19 |
|
| 20 |
+
Supports multiple AI providers via LiteLLM (as of January 2025):
|
| 21 |
+
|
| 22 |
+
Anthropic models:
|
| 23 |
- Claude-4: claude-opus-4-20250514, claude-sonnet-4-20250514 (Premium)
|
| 24 |
- Claude-3.7: claude-3-7-sonnet-20250219 (Extended thinking)
|
| 25 |
- Claude-3.5: claude-3-5-sonnet-20241022 (latest), claude-3-5-sonnet-20240620 (stable), claude-3-5-haiku-20241022 (fast)
|
| 26 |
- Claude-3: claude-3-haiku-20240307 (legacy but reliable)
|
| 27 |
|
| 28 |
+
OpenAI models:
|
| 29 |
+
- GPT-4o: gpt-4o, gpt-4o-mini (Vision + latest capabilities)
|
| 30 |
+
- GPT-4: gpt-4-turbo (Legacy but stable)
|
| 31 |
+
- GPT-3.5: gpt-3.5-turbo (Cost-effective)
|
| 32 |
+
- Reasoning: o1-preview, o1-mini, o3-mini (Advanced reasoning)
|
| 33 |
+
|
| 34 |
+
Note: Some older models may be deprecated. Always check provider documentation.
|
| 35 |
"""
|
| 36 |
|
| 37 |
+
# Available models via LiteLLM and native OpenAI
|
| 38 |
+
# Updated to include both Anthropic and OpenAI models as of January 2025
|
| 39 |
SUPPORTED_MODELS = {
|
| 40 |
+
# === ANTHROPIC MODELS (via LiteLLM) ===
|
| 41 |
# Claude-4 models (latest generation - may require special access)
|
| 42 |
+
"claude-4-opus": "litellm/anthropic/claude-opus-4-20250514",
|
| 43 |
+
"claude-4-sonnet": "litellm/anthropic/claude-sonnet-4-20250514",
|
| 44 |
|
| 45 |
# Claude-3.7 models (newest stable)
|
| 46 |
+
"claude-3.7-sonnet": "litellm/anthropic/claude-3-7-sonnet-20250219",
|
| 47 |
|
| 48 |
# Claude-3.5 models (widely available)
|
| 49 |
+
"claude-3.5-sonnet-latest": "litellm/anthropic/claude-3-5-sonnet-20241022", # Latest version
|
| 50 |
+
"claude-3.5-sonnet": "litellm/anthropic/claude-3-5-sonnet-20240620", # Previous stable version
|
| 51 |
+
"claude-3.5-haiku": "litellm/anthropic/claude-3-5-haiku-20241022", # New Haiku 3.5 model
|
| 52 |
|
| 53 |
# Claude-3 models (legacy but still available)
|
| 54 |
+
"claude-3-haiku": "litellm/anthropic/claude-3-haiku-20240307",
|
| 55 |
+
|
| 56 |
+
# === OPENAI MODELS (native) ===
|
| 57 |
+
# GPT-4o models (latest generation with vision)
|
| 58 |
+
"gpt-4o": "gpt-4o", # Latest GPT-4o model
|
| 59 |
+
"gpt-4o-mini": "gpt-4o-mini", # Compact version
|
| 60 |
+
|
| 61 |
+
# GPT-4 models (previous generation)
|
| 62 |
+
"gpt-4-turbo": "gpt-4-turbo", # Latest GPT-4 Turbo
|
| 63 |
+
"gpt-4": "gpt-4", # Original GPT-4
|
| 64 |
+
|
| 65 |
+
# GPT-3.5 models (cost-effective)
|
| 66 |
+
"gpt-3.5-turbo": "gpt-3.5-turbo", # Latest 3.5 turbo
|
| 67 |
+
|
| 68 |
+
# Reasoning models (o-series)
|
| 69 |
+
"o1-preview": "o1-preview", # Advanced reasoning
|
| 70 |
+
"o1-mini": "o1-mini", # Compact reasoning
|
| 71 |
+
"o3-mini": "o3-mini", # Latest reasoning model
|
| 72 |
}
|
| 73 |
|
| 74 |
def __init__(self, model_name: Optional[str] = None):
|
| 75 |
"""
|
| 76 |
+
Initialize the Fitness Agent with configurable AI model (Anthropic or OpenAI).
|
| 77 |
|
| 78 |
Args:
|
| 79 |
+
model_name: Name of the AI model to use. Can be a key from SUPPORTED_MODELS
|
| 80 |
+
or a full model identifier. Defaults to claude-3.5-haiku if not specified.
|
| 81 |
+
Can also be set via AI_MODEL, ANTHROPIC_MODEL, or OPENAI_MODEL environment variables.
|
| 82 |
"""
|
| 83 |
# Determine which model to use
|
| 84 |
if model_name is None:
|
| 85 |
+
# Check environment variables in priority order
|
| 86 |
+
model_name = (
|
| 87 |
+
os.getenv("AI_MODEL") or
|
| 88 |
+
os.getenv("ANTHROPIC_MODEL") or
|
| 89 |
+
os.getenv("OPENAI_MODEL") or
|
| 90 |
+
"claude-3.5-haiku" # Default fallback
|
| 91 |
+
)
|
| 92 |
|
| 93 |
# Validate the model name
|
| 94 |
is_valid, validation_message = self.validate_model_name(model_name)
|
|
|
|
| 104 |
# Assume it's already a full model identifier
|
| 105 |
full_model_name = model_name
|
| 106 |
|
| 107 |
+
# Determine if this is an OpenAI model or needs LiteLLM prefix
|
| 108 |
+
if self._is_openai_model(model_name, full_model_name):
|
| 109 |
+
# Use native OpenAI model (no prefix needed)
|
| 110 |
+
final_model = full_model_name
|
| 111 |
else:
|
| 112 |
+
# For Anthropic models, use the full model name as-is since it already has litellm/ prefix
|
| 113 |
+
final_model = full_model_name
|
| 114 |
|
| 115 |
# Store the model information for debugging
|
| 116 |
self.model_name = model_name
|
| 117 |
self.full_model_name = full_model_name
|
| 118 |
+
self.final_model = final_model
|
| 119 |
+
self.provider = self._get_provider(model_name, full_model_name)
|
| 120 |
|
| 121 |
fitness_plan_agent = Agent(
|
| 122 |
name="Fitness Plan Assistant",
|
| 123 |
instructions="You are a helpful assistant for creating personalized fitness plans.",
|
| 124 |
+
model=final_model,
|
| 125 |
output_type=FitnessPlan
|
| 126 |
)
|
| 127 |
|
| 128 |
super().__init__(
|
| 129 |
name="Fitness Assistant",
|
| 130 |
+
model=final_model,
|
| 131 |
instructions="""
|
| 132 |
You are a helpful assistant for fitness-related queries.
|
| 133 |
|
|
|
|
| 136 |
handoffs=[fitness_plan_agent]
|
| 137 |
)
|
| 138 |
|
| 139 |
+
def _is_openai_model(self, model_name: str, full_model_name: str) -> bool:
|
| 140 |
+
"""Check if this is an OpenAI model that should use native API."""
|
| 141 |
+
# Check direct model name matches
|
| 142 |
+
openai_models = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo", "o1-preview", "o1-mini", "o3-mini"]
|
| 143 |
+
if model_name in openai_models:
|
| 144 |
+
return True
|
| 145 |
+
|
| 146 |
+
# Check for OpenAI indicators in model names
|
| 147 |
+
openai_prefixes = ["gpt-", "o1-", "o3-", "openai/", "litellm/openai/"]
|
| 148 |
+
for prefix in openai_prefixes:
|
| 149 |
+
if prefix in model_name.lower() or prefix in full_model_name.lower():
|
| 150 |
+
return True
|
| 151 |
+
|
| 152 |
+
return False
|
| 153 |
+
|
| 154 |
+
def _get_provider(self, model_name: str, full_model_name: str) -> str:
|
| 155 |
+
"""Determine the provider based on the model."""
|
| 156 |
+
if self._is_openai_model(model_name, full_model_name):
|
| 157 |
+
return "openai"
|
| 158 |
+
elif "claude" in model_name.lower() or "anthropic" in full_model_name.lower():
|
| 159 |
+
return "anthropic"
|
| 160 |
+
else:
|
| 161 |
+
return "unknown"
|
| 162 |
+
|
| 163 |
@classmethod
|
| 164 |
def list_supported_models(cls) -> dict:
|
| 165 |
"""Return a dictionary of supported model names and their full identifiers."""
|
|
|
|
| 169 |
def get_model_info(cls, model_name: str) -> str:
|
| 170 |
"""Get information about a specific model."""
|
| 171 |
model_info = {
|
| 172 |
+
# === ANTHROPIC MODELS ===
|
| 173 |
"claude-4-opus": "Most capable and intelligent model. Superior reasoning, complex tasks (Premium tier)",
|
| 174 |
+
"claude-4-sonnet": "High-performance model with exceptional reasoning and efficiency (Premium tier)",
|
| 175 |
"claude-3.7-sonnet": "Enhanced model with extended thinking capabilities (Recommended)",
|
| 176 |
"claude-3.5-sonnet-latest": "Latest Claude 3.5 Sonnet with improved capabilities (Recommended)",
|
| 177 |
"claude-3.5-sonnet": "Excellent balance of intelligence and speed (Stable version)",
|
| 178 |
"claude-3.5-haiku": "Fast and compact model for near-instant responsiveness (New!)",
|
| 179 |
"claude-3-haiku": "Fastest model, good for simple tasks and cost-effective (Legacy but reliable)",
|
| 180 |
+
|
| 181 |
+
# === OPENAI MODELS ===
|
| 182 |
+
"gpt-4o": "Latest GPT-4o with vision, web browsing, and advanced capabilities (Recommended)",
|
| 183 |
+
"gpt-4o-mini": "Compact GPT-4o model - fast, capable, and cost-effective (Recommended)",
|
| 184 |
+
"gpt-4-turbo": "GPT-4 Turbo with large context window and improved efficiency",
|
| 185 |
+
"gpt-4": "Original GPT-4 model - highly capable but slower than turbo variants",
|
| 186 |
+
"gpt-3.5-turbo": "Fast and cost-effective model, good for straightforward tasks",
|
| 187 |
+
"o1-preview": "Advanced reasoning model with enhanced problem-solving (Preview)",
|
| 188 |
+
"o1-mini": "Compact reasoning model for faster inference with good capabilities",
|
| 189 |
+
"o3-mini": "Latest reasoning model with improved performance (New!)",
|
| 190 |
}
|
| 191 |
return model_info.get(model_name, "Model information not available")
|
| 192 |
|
|
|
|
| 194 |
def get_recommended_models(cls) -> list:
|
| 195 |
"""Get a list of recommended models that are most likely to be available."""
|
| 196 |
return [
|
| 197 |
+
# Anthropic recommendations (most reliable first)
|
| 198 |
+
"claude-3.5-haiku", # New fast model, default
|
| 199 |
+
"claude-3-haiku", # Most reliable, widely available, cost-effective
|
| 200 |
"claude-3.5-sonnet", # Stable version, widely available
|
| 201 |
"claude-3.5-sonnet-latest", # Latest improvements
|
| 202 |
"claude-3.7-sonnet", # Newest stable with extended thinking
|
| 203 |
+
|
| 204 |
+
# OpenAI recommendations
|
| 205 |
+
"gpt-4o-mini", # Best balance of capability and cost
|
| 206 |
+
"gpt-4o", # Latest flagship model
|
| 207 |
+
"gpt-3.5-turbo", # Most cost-effective OpenAI model
|
| 208 |
+
"gpt-4-turbo", # Solid previous generation
|
| 209 |
+
"o1-mini", # Good reasoning capabilities
|
| 210 |
]
|
| 211 |
|
| 212 |
+
@classmethod
|
| 213 |
+
def get_models_by_provider(cls) -> dict:
|
| 214 |
+
"""Get models organized by provider."""
|
| 215 |
+
models = cls.list_supported_models()
|
| 216 |
+
providers = {
|
| 217 |
+
"anthropic": {},
|
| 218 |
+
"openai": {},
|
| 219 |
+
"unknown": {}
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
for name, full_name in models.items():
|
| 223 |
+
if "claude" in name.lower() or "anthropic" in full_name.lower():
|
| 224 |
+
providers["anthropic"][name] = full_name
|
| 225 |
+
elif any(indicator in name.lower() for indicator in ["gpt-", "o1-", "o3-", "openai/"]):
|
| 226 |
+
providers["openai"][name] = full_name
|
| 227 |
+
else:
|
| 228 |
+
providers["unknown"][name] = full_name
|
| 229 |
+
|
| 230 |
+
return providers
|
| 231 |
+
|
| 232 |
+
@classmethod
|
| 233 |
+
def get_models_table_data(cls) -> list:
|
| 234 |
+
"""Get model data formatted for table display."""
|
| 235 |
+
models = cls.list_supported_models()
|
| 236 |
+
table_data = []
|
| 237 |
+
|
| 238 |
+
# Define capability ratings
|
| 239 |
+
capability_ratings = {
|
| 240 |
+
# Anthropic models
|
| 241 |
+
"claude-4-opus": "β
β
β
β
β
",
|
| 242 |
+
"claude-4-sonnet": "β
β
β
β
β",
|
| 243 |
+
"claude-3.7-sonnet": "β
β
β
β
β",
|
| 244 |
+
"claude-3.5-sonnet-latest": "β
β
β
β
β",
|
| 245 |
+
"claude-3.5-sonnet": "β
β
β
β
β",
|
| 246 |
+
"claude-3.5-haiku": "β
β
β
ββ",
|
| 247 |
+
"claude-3-haiku": "β
β
β
ββ",
|
| 248 |
+
# OpenAI models
|
| 249 |
+
"gpt-4o": "β
β
β
β
β
",
|
| 250 |
+
"gpt-4o-mini": "β
β
β
β
β",
|
| 251 |
+
"gpt-4-turbo": "β
β
β
β
β",
|
| 252 |
+
"gpt-4": "β
β
β
β
β",
|
| 253 |
+
"gpt-3.5-turbo": "β
β
β
ββ",
|
| 254 |
+
"o1-preview": "β
β
β
β
β
",
|
| 255 |
+
"o1-mini": "β
β
β
β
β",
|
| 256 |
+
"o3-mini": "β
β
β
β
β",
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
# Define speed ratings
|
| 260 |
+
speed_ratings = {
|
| 261 |
+
# Anthropic models
|
| 262 |
+
"claude-4-opus": "β
β
β
ββ",
|
| 263 |
+
"claude-4-sonnet": "β
β
β
β
β",
|
| 264 |
+
"claude-3.7-sonnet": "β
β
β
β
β",
|
| 265 |
+
"claude-3.5-sonnet-latest": "β
β
β
β
β",
|
| 266 |
+
"claude-3.5-sonnet": "β
β
β
β
β",
|
| 267 |
+
"claude-3.5-haiku": "β
β
β
β
β
",
|
| 268 |
+
"claude-3-haiku": "β
β
β
β
β
",
|
| 269 |
+
# OpenAI models
|
| 270 |
+
"gpt-4o": "β
β
β
β
β",
|
| 271 |
+
"gpt-4o-mini": "β
β
β
β
β
",
|
| 272 |
+
"gpt-4-turbo": "β
β
β
β
β",
|
| 273 |
+
"gpt-4": "β
β
β
ββ",
|
| 274 |
+
"gpt-3.5-turbo": "β
β
β
β
β
",
|
| 275 |
+
"o1-preview": "β
β
βββ",
|
| 276 |
+
"o1-mini": "β
β
β
ββ",
|
| 277 |
+
"o3-mini": "β
β
β
β
β",
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
# Define cost ratings (more stars = more expensive)
|
| 281 |
+
cost_ratings = {
|
| 282 |
+
# Anthropic models
|
| 283 |
+
"claude-4-opus": "β
β
β
β
β
",
|
| 284 |
+
"claude-4-sonnet": "β
β
β
β
β",
|
| 285 |
+
"claude-3.7-sonnet": "β
β
β
ββ",
|
| 286 |
+
"claude-3.5-sonnet-latest": "β
β
β
ββ",
|
| 287 |
+
"claude-3.5-sonnet": "β
β
β
ββ",
|
| 288 |
+
"claude-3.5-haiku": "β
β
βββ",
|
| 289 |
+
"claude-3-haiku": "β
ββββ",
|
| 290 |
+
# OpenAI models
|
| 291 |
+
"gpt-4o": "β
β
β
β
β",
|
| 292 |
+
"gpt-4o-mini": "β
β
βββ",
|
| 293 |
+
"gpt-4-turbo": "β
β
β
β
β",
|
| 294 |
+
"gpt-4": "β
β
β
β
β",
|
| 295 |
+
"gpt-3.5-turbo": "β
ββββ",
|
| 296 |
+
"o1-preview": "β
β
β
β
β
",
|
| 297 |
+
"o1-mini": "β
β
β
ββ",
|
| 298 |
+
"o3-mini": "β
β
β
ββ",
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
recommended = cls.get_recommended_models()
|
| 302 |
+
|
| 303 |
+
for model_name, full_path in models.items():
|
| 304 |
+
provider = "π΅ Anthropic" if "claude" in model_name.lower() else "π’ OpenAI"
|
| 305 |
+
is_recommended = "β" if model_name in recommended else ""
|
| 306 |
+
|
| 307 |
+
table_data.append([
|
| 308 |
+
is_recommended,
|
| 309 |
+
provider,
|
| 310 |
+
model_name,
|
| 311 |
+
capability_ratings.get(model_name, "β
β
β
ββ"),
|
| 312 |
+
speed_ratings.get(model_name, "β
β
β
ββ"),
|
| 313 |
+
cost_ratings.get(model_name, "β
β
β
ββ"),
|
| 314 |
+
cls.get_model_info(model_name)
|
| 315 |
+
])
|
| 316 |
+
|
| 317 |
+
return table_data
|
| 318 |
+
|
| 319 |
@classmethod
|
| 320 |
def validate_model_name(cls, model_name: str) -> tuple[bool, str]:
|
| 321 |
"""
|
|
|
|
| 336 |
|
| 337 |
if __name__ == "__main__":
|
| 338 |
# Example usage with different models
|
| 339 |
+
print("π€ Available AI Models (Anthropic + OpenAI):")
|
| 340 |
+
print("=" * 60)
|
| 341 |
+
|
| 342 |
+
# Show models by provider
|
| 343 |
+
providers = FitnessAgent.get_models_by_provider()
|
| 344 |
+
|
| 345 |
+
print("π΅ ANTHROPIC MODELS:")
|
| 346 |
+
for name, full_id in providers["anthropic"].items():
|
| 347 |
+
print(f" β’ {name}: {full_id}")
|
| 348 |
+
print(f" {FitnessAgent.get_model_info(name)}")
|
| 349 |
+
print()
|
| 350 |
+
|
| 351 |
+
print("π’ OPENAI MODELS:")
|
| 352 |
+
for name, full_id in providers["openai"].items():
|
| 353 |
+
print(f" β’ {name}: {full_id}")
|
| 354 |
print(f" {FitnessAgent.get_model_info(name)}")
|
| 355 |
+
print()
|
| 356 |
+
|
| 357 |
+
print("π― RECOMMENDED MODELS (most likely to work):")
|
| 358 |
+
recommended = FitnessAgent.get_recommended_models()
|
| 359 |
+
for model in recommended:
|
| 360 |
+
provider_icon = "π΅" if "claude" in model else "π’" if any(x in model for x in ["gpt", "o1", "o3"]) else "βͺ"
|
| 361 |
+
print(f" {provider_icon} {model}")
|
| 362 |
|
| 363 |
+
print("\n" + "="*60 + "\n")
|
| 364 |
|
| 365 |
+
# Create agent with default model
|
| 366 |
+
print("Creating agent with default model (claude-3.5-haiku)...")
|
| 367 |
agent = FitnessAgent()
|
| 368 |
+
print(f"β
Created agent:")
|
| 369 |
+
print(f" Model name: {agent.model_name}")
|
| 370 |
+
print(f" Provider: {agent.provider}")
|
| 371 |
+
print(f" Final model: {agent.final_model}")
|
| 372 |
+
|
| 373 |
+
print("\n" + "="*60 + "\n")
|
| 374 |
+
|
| 375 |
+
# Example with OpenAI model
|
| 376 |
+
print("Creating agent with OpenAI model (gpt-4o-mini)...")
|
| 377 |
+
try:
|
| 378 |
+
openai_agent = FitnessAgent("gpt-4o-mini")
|
| 379 |
+
print(f"β
Created OpenAI agent:")
|
| 380 |
+
print(f" Model name: {openai_agent.model_name}")
|
| 381 |
+
print(f" Provider: {openai_agent.provider}")
|
| 382 |
+
print(f" Final model: {openai_agent.final_model}")
|
| 383 |
+
except Exception as e:
|
| 384 |
+
print(f"β οΈ Could not create OpenAI agent: {e}")
|
| 385 |
+
print(" (This is normal if you don't have OPENAI_API_KEY set)")
|
| 386 |
|
| 387 |
+
print("\nπ‘ To actually run the agents:")
|
| 388 |
+
print(" - Set ANTHROPIC_API_KEY for Claude models")
|
| 389 |
+
print(" - Set OPENAI_API_KEY for GPT models")
|
| 390 |
+
print(" - Use Runner.run_sync(agent, 'your message') to chat")
|
| 391 |
|
| 392 |
+
# Uncomment this to test with actual API call:
|
| 393 |
+
# result = Runner.run_sync(agent, "Hello. Please make me a fitness plan.")
|
| 394 |
+
# print(result.final_output)
|
fitness_agent/test_openai_models.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test script to verify OpenAI model integration with the Fitness Agent.
|
| 3 |
+
This will help identify which models are available and test the integration.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import sys
|
| 7 |
+
import os
|
| 8 |
+
|
| 9 |
+
# Add the current directory to Python path
|
| 10 |
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
| 11 |
+
|
| 12 |
+
from fitness_agent import FitnessAgent
|
| 13 |
+
|
| 14 |
+
def test_openai_models():
|
| 15 |
+
"""Test OpenAI model availability and integration"""
|
| 16 |
+
print("π§ͺ Testing OpenAI Model Integration")
|
| 17 |
+
print("=" * 60)
|
| 18 |
+
|
| 19 |
+
# Get OpenAI models specifically
|
| 20 |
+
providers = FitnessAgent.get_models_by_provider()
|
| 21 |
+
openai_models = providers["openai"]
|
| 22 |
+
|
| 23 |
+
if not openai_models:
|
| 24 |
+
print("β No OpenAI models found in configuration!")
|
| 25 |
+
return
|
| 26 |
+
|
| 27 |
+
print("π Configured OpenAI Models:")
|
| 28 |
+
for name, full_id in openai_models.items():
|
| 29 |
+
info = FitnessAgent.get_model_info(name)
|
| 30 |
+
print(f" β’ {name}: {full_id}")
|
| 31 |
+
print(f" {info}")
|
| 32 |
+
print()
|
| 33 |
+
|
| 34 |
+
# Test recommended OpenAI models
|
| 35 |
+
recommended = FitnessAgent.get_recommended_models()
|
| 36 |
+
openai_recommended = [m for m in recommended if any(x in m for x in ["gpt", "o1", "o3"])]
|
| 37 |
+
|
| 38 |
+
print("π― Recommended OpenAI Models:")
|
| 39 |
+
for model in openai_recommended:
|
| 40 |
+
print(f" π’ {model}")
|
| 41 |
+
|
| 42 |
+
print("\n" + "=" * 60)
|
| 43 |
+
|
| 44 |
+
# Test creating agents with different OpenAI models
|
| 45 |
+
test_models = ["gpt-4o-mini", "gpt-4o", "gpt-3.5-turbo", "o1-mini"]
|
| 46 |
+
|
| 47 |
+
for model in test_models:
|
| 48 |
+
if model in openai_models:
|
| 49 |
+
print(f"π Testing agent creation with {model}...")
|
| 50 |
+
try:
|
| 51 |
+
agent = FitnessAgent(model)
|
| 52 |
+
print(f"β
Successfully created agent:")
|
| 53 |
+
print(f" Model name: {agent.model_name}")
|
| 54 |
+
print(f" Provider: {agent.provider}")
|
| 55 |
+
print(f" Final model: {agent.final_model}")
|
| 56 |
+
print(f" Expected format: {openai_models[model]}")
|
| 57 |
+
|
| 58 |
+
# Verify the model format is correct
|
| 59 |
+
if agent.provider == "openai":
|
| 60 |
+
print("β
Correctly identified as OpenAI model")
|
| 61 |
+
else:
|
| 62 |
+
print(f"β οΈ Provider detection issue: expected 'openai', got '{agent.provider}'")
|
| 63 |
+
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f"β Error creating agent with {model}: {str(e)}")
|
| 66 |
+
if "api" in str(e).lower() or "key" in str(e).lower():
|
| 67 |
+
print(" π‘ This might be due to missing OPENAI_API_KEY")
|
| 68 |
+
|
| 69 |
+
print()
|
| 70 |
+
|
| 71 |
+
def test_model_detection():
|
| 72 |
+
"""Test the model provider detection logic"""
|
| 73 |
+
print("π Testing Model Provider Detection")
|
| 74 |
+
print("=" * 60)
|
| 75 |
+
|
| 76 |
+
test_cases = [
|
| 77 |
+
("gpt-4o", "openai"),
|
| 78 |
+
("gpt-4o-mini", "openai"),
|
| 79 |
+
("gpt-3.5-turbo", "openai"),
|
| 80 |
+
("o1-preview", "openai"),
|
| 81 |
+
("o3-mini", "openai"),
|
| 82 |
+
("claude-3.5-haiku", "anthropic"),
|
| 83 |
+
("claude-4-opus", "anthropic"),
|
| 84 |
+
("openai/gpt-4o", "openai"),
|
| 85 |
+
]
|
| 86 |
+
|
| 87 |
+
for model, expected_provider in test_cases:
|
| 88 |
+
try:
|
| 89 |
+
agent = FitnessAgent(model)
|
| 90 |
+
actual_provider = agent.provider
|
| 91 |
+
status = "β
" if actual_provider == expected_provider else "β"
|
| 92 |
+
print(f"{status} {model}: expected {expected_provider}, got {actual_provider}")
|
| 93 |
+
except Exception as e:
|
| 94 |
+
print(f"β {model}: Error - {str(e)}")
|
| 95 |
+
|
| 96 |
+
def check_environment():
|
| 97 |
+
"""Check environment setup for OpenAI"""
|
| 98 |
+
print("\nπ§ Environment Check")
|
| 99 |
+
print("=" * 60)
|
| 100 |
+
|
| 101 |
+
openai_key = os.getenv("OPENAI_API_KEY")
|
| 102 |
+
anthropic_key = os.getenv("ANTHROPIC_API_KEY")
|
| 103 |
+
ai_model = os.getenv("AI_MODEL")
|
| 104 |
+
|
| 105 |
+
print(f"OPENAI_API_KEY: {'β
Set' if openai_key else 'β Not set'}")
|
| 106 |
+
print(f"ANTHROPIC_API_KEY: {'β
Set' if anthropic_key else 'β Not set'}")
|
| 107 |
+
print(f"AI_MODEL: {ai_model if ai_model else 'Not set (will use default)'}")
|
| 108 |
+
|
| 109 |
+
if not openai_key and not anthropic_key:
|
| 110 |
+
print("\nβ οΈ No API keys found! Set at least one in your .env file:")
|
| 111 |
+
print(" OPENAI_API_KEY=your_openai_key_here")
|
| 112 |
+
print(" ANTHROPIC_API_KEY=your_anthropic_key_here")
|
| 113 |
+
elif openai_key and not anthropic_key:
|
| 114 |
+
print("\nπ’ OpenAI setup detected - you can use GPT models")
|
| 115 |
+
elif anthropic_key and not openai_key:
|
| 116 |
+
print("\nπ΅ Anthropic setup detected - you can use Claude models")
|
| 117 |
+
else:
|
| 118 |
+
print("\nπ Both providers configured - you can use all models!")
|
| 119 |
+
|
| 120 |
+
def show_usage_examples():
|
| 121 |
+
"""Show examples of how to use OpenAI models"""
|
| 122 |
+
print("\nπ Usage Examples")
|
| 123 |
+
print("=" * 60)
|
| 124 |
+
|
| 125 |
+
examples = [
|
| 126 |
+
("Basic OpenAI", "FitnessAgent('gpt-4o-mini')"),
|
| 127 |
+
("Latest GPT-4o", "FitnessAgent('gpt-4o')"),
|
| 128 |
+
("Cost-effective", "FitnessAgent('gpt-3.5-turbo')"),
|
| 129 |
+
("Reasoning", "FitnessAgent('o1-mini')"),
|
| 130 |
+
("Environment var", "os.environ['AI_MODEL'] = 'gpt-4o'; FitnessAgent()"),
|
| 131 |
+
("Via prefix", "FitnessAgent('openai/gpt-4o-mini')"),
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
for name, code in examples:
|
| 135 |
+
print(f"# {name}")
|
| 136 |
+
print(f"agent = {code}")
|
| 137 |
+
print()
|
| 138 |
+
|
| 139 |
+
if __name__ == "__main__":
|
| 140 |
+
check_environment()
|
| 141 |
+
test_openai_models()
|
| 142 |
+
test_model_detection()
|
| 143 |
+
show_usage_examples()
|
| 144 |
+
|
| 145 |
+
print("=" * 60)
|
| 146 |
+
print("π― Summary:")
|
| 147 |
+
print(" β’ OpenAI models are now supported alongside Anthropic")
|
| 148 |
+
print(" β’ Use gpt-4o-mini for balanced performance")
|
| 149 |
+
print(" β’ Use claude-3.5-haiku for fast responses")
|
| 150 |
+
print(" β’ Set OPENAI_API_KEY and/or ANTHROPIC_API_KEY in .env")
|
| 151 |
+
print(" β’ Launch with: python app.py")
|