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 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
- # OpenAI API Key (or other LLM provider)
 
5
  OPENAI_API_KEY=your_openai_api_key_here
6
 
7
- # Alternative: If using other providers through LiteLLM
 
 
 
 
 
 
8
  # ANTHROPIC_API_KEY=your_anthropic_key_here
9
- # GOOGLE_API_KEY=your_google_key_here
 
 
 
 
 
 
 
 
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's Claude 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 Models**: Choose from Claude-3.5-Haiku to Claude-4-Opus based on your needs
 
23
  - **πŸ’¬ Interactive Chat**: Conversational interface with memory and context
24
- - **⚑ Real-time Streaming**: See responses generated live
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- # Edit .env and add your ANTHROPIC_API_KEY
 
 
 
 
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 (default: claude-3.5-haiku)
 
 
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
- # Check if it's a "not found" error specifically
120
  error_str = str(model_error)
121
- if "not_found_error" in error_str or "NotFoundError" in error_str:
 
 
 
 
 
 
 
 
 
 
 
 
 
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 Anthropic API account.
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
- πŸ“ **Details:** {str(model_error)}
 
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
- if selected_model == "--- Legacy/Experimental ---":
178
- return """⚠️ **Please select a specific model**
 
 
 
 
 
 
179
 
180
- The separator "--- Legacy/Experimental ---" is not a valid model choice.
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
- return f"""πŸ€– **Current Model:** `{selected_model}`
 
 
 
 
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(scale=1):
948
- # Get available models for dropdown - prioritize recommended models
949
- all_models = FitnessAgent.list_supported_models()
950
- recommended_models = FitnessAgent.get_recommended_models()
951
 
952
- # Create choices list with recommended models first, then others
953
- other_models = [m for m in all_models.keys() if m not in recommended_models]
954
- model_choices = recommended_models + ["--- Legacy/Experimental ---"] + other_models
955
 
956
- model_dropdown = gr.Dropdown(
957
- choices=model_choices,
958
- value="claude-3.5-haiku", # Updated default model
959
- label="πŸ€– AI Model",
960
- info="Choose which Anthropic model to use (recommended models listed first)",
961
- interactive=True,
962
- elem_classes=["model-dropdown"]
963
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
964
 
965
  # Model information display
966
  model_info_display = gr.Markdown(
967
- value=f"""πŸ€– **Current Model:** `claude-3.5-haiku`
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
- - **Claude-4 Models**: Most capable, best for complex reasoning and detailed plans (higher cost)
1036
- - **Claude-3.5/3.7**: Excellent balance of capability and speed (recommended for most users)
1037
- - **Claude-3 Haiku**: Fastest and most cost-effective (good for simple questions)
1038
- - **Claude-2/Instant**: Previous generation models (basic functionality)
 
 
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
- model_comparison = "| Model | Capability | Speed | Cost | Best For |\n"
1058
- model_comparison += "|-------|------------|--------|------|----------|\n"
1059
-
1060
- models_info = {
1061
- "claude-4-opus": ("β˜…β˜…β˜…β˜…β˜…", "β˜…β˜…β˜…β˜†β˜†", "β˜…β˜…β˜…β˜…β˜…", "Complex analysis, detailed plans"),
1062
- "claude-4-sonnet": ("β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜…β˜†", "Balanced performance"),
1063
- "claude-3.7-sonnet": ("β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜†β˜†", "Enhanced capabilities"),
1064
- "claude-3.5-sonnet": ("β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜†β˜†", "General use, balanced"),
1065
- "claude-3-opus": ("β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜†β˜†", "β˜…β˜…β˜…β˜…β˜†", "Complex tasks"),
1066
- "claude-3-sonnet": ("β˜…β˜…β˜…β˜†β˜†", "β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜…β˜†β˜†", "Standard tasks"),
1067
- "claude-3-haiku": ("β˜…β˜…β˜…β˜†β˜†", "β˜…β˜…β˜…β˜…β˜…", "β˜…β˜…β˜†β˜†β˜†", "Quick questions, cost-effective"),
1068
- "claude-2.1": ("β˜…β˜…β˜†β˜†β˜†", "β˜…β˜…β˜…β˜…β˜†", "β˜…β˜…β˜†β˜†β˜†", "Basic functionality"),
1069
- }
1070
-
1071
- for model, (capability, speed, cost, best_for) in models_info.items():
1072
- model_comparison += f"| {model} | {capability} | {speed} | {cost} | {best_for} |\n"
1073
-
1074
- gr.Markdown(model_comparison)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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, model_dropdown],
1083
  chatbot,
1084
  api_name="bot_response"
1085
  )
1086
  bot_msg.then(lambda: gr.MultimodalTextbox(interactive=True), None, [chat_input])
1087
 
1088
- # Update model and display when dropdown selection changes
1089
- model_dropdown.change(
1090
- update_model_and_display,
1091
- inputs=[model_dropdown],
1092
- outputs=[model_info_display]
 
 
 
 
 
 
 
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 Anthropic models.
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 specific_model_example():
24
- """Example using a specific model."""
25
- print("=== Using Specific Model ===")
26
  agent = FitnessAgent("claude-3.5-sonnet")
27
  print(f"Using model: {agent.model_name}")
28
- print("Agent created successfully!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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["ANTHROPIC_MODEL"] = "claude-3.7-sonnet"
36
 
37
  agent = FitnessAgent() # Will use the model from environment variable
38
  print(f"Using model: {agent.model_name}")
39
- print("Agent created successfully!")
 
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
- print("Recommended models:")
48
- for model in FitnessAgent.get_recommended_models():
49
- full_name = models.get(model, "Not found")
 
 
 
 
 
 
 
 
 
 
 
50
  info = FitnessAgent.get_model_info(model)
51
- print(f" β€’ {model}: {full_name}")
52
- print(f" {info}")
 
53
  print()
54
 
55
- print("All available models:")
56
- for name, full_id in models.items():
57
- print(f" β€’ {name}: {full_id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  print()
59
 
60
  if __name__ == "__main__":
61
- print("πŸ€– Fitness Agent Examples")
62
- print("=" * 50)
63
 
64
  list_available_models()
 
65
  basic_example()
66
- specific_model_example()
 
 
67
  environment_variable_example()
68
 
 
69
  print("βœ… All examples completed!")
70
- print("\nTo actually run the agent, uncomment the Runner.run_sync lines")
71
- print("and make sure you have your ANTHROPIC_API_KEY set in .env")
 
 
 
 
 
 
 
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 current LiteLLM Anthropic models (as of January 2025):
 
 
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
- Note: Some older models (claude-3-opus, claude-3-sonnet, claude-2.x, claude-instant)
27
- have been deprecated and removed from the API.
 
 
 
 
 
28
  """
29
 
30
- # Available Anthropic models via LiteLLM
31
- # Updated to match current Anthropic API as of January 2025
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
- # Note: claude-3-opus and claude-3-sonnet have been deprecated/removed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  def __init__(self, model_name: Optional[str] = None):
51
  """
52
- Initialize the Fitness Agent with configurable Anthropic model.
53
 
54
  Args:
55
- model_name: Name of the Anthropic model to use. Can be a key from SUPPORTED_MODELS
56
- or a full model identifier. Defaults to claude-3-haiku if not specified.
57
- Can also be set via ANTHROPIC_MODEL environment variable.
58
  """
59
  # Determine which model to use
60
  if model_name is None:
61
- # Check environment variable first, default to the newest fast model
62
- model_name = os.getenv("ANTHROPIC_MODEL", "claude-3.5-haiku")
 
 
 
 
 
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
- # Add litellm/anthropic/ prefix if not already present
79
- if not full_model_name.startswith("litellm/anthropic/"):
80
- litellm_model = f"litellm/anthropic/{full_model_name}"
 
81
  else:
82
- litellm_model = full_model_name
 
83
 
84
  # Store the model information for debugging
85
  self.model_name = model_name
86
  self.full_model_name = full_model_name
87
- self.litellm_model = litellm_model
 
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=litellm_model,
93
  output_type=FitnessPlan
94
  )
95
 
96
  super().__init__(
97
  name="Fitness Assistant",
98
- model=litellm_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
- "claude-3-haiku", # Most reliable, widely available, cost-effective
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 models:")
158
- for name, full_id in FitnessAgent.list_supported_models().items():
159
- print(f" {name}: {full_id}")
 
 
 
 
 
 
 
 
 
 
 
 
160
  print(f" {FitnessAgent.get_model_info(name)}")
 
 
 
 
 
 
 
161
 
162
- print("\n" + "="*50 + "\n")
163
 
164
- # Create agent with new default model (claude-3.5-haiku)
165
- print("Creating agent with new default model (claude-3.5-haiku)...")
166
  agent = FitnessAgent()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
- # You can also specify a different model:
169
- # agent = FitnessAgent("claude-3.5-sonnet") # Using friendly name
170
- # agent = FitnessAgent("claude-3-5-sonnet-20241022") # Using full identifier
 
171
 
172
- result = Runner.run_sync(agent, "Hello. Please make me a fitness plan.")
173
- print(result.final_output)
 
 
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")