sizzlebop commited on
Commit
4d85aba
Β·
verified Β·
1 Parent(s): 583a8ef

Upload 34 files

Browse files
app.py CHANGED
@@ -45,7 +45,7 @@ def main():
45
  launch_kwargs = {
46
  "server_name": "0.0.0.0",
47
  "server_port": 7860,
48
- "share": True,
49
  "show_error": True,
50
  "quiet": False
51
  }
 
45
  launch_kwargs = {
46
  "server_name": "0.0.0.0",
47
  "server_port": 7860,
48
+ "share": False,
49
  "show_error": True,
50
  "quiet": False
51
  }
coursecrafter/agents/__pycache__/llm_client.cpython-311.pyc CHANGED
Binary files a/coursecrafter/agents/__pycache__/llm_client.cpython-311.pyc and b/coursecrafter/agents/__pycache__/llm_client.cpython-311.pyc differ
 
coursecrafter/agents/__pycache__/simple_course_agent.cpython-311.pyc CHANGED
Binary files a/coursecrafter/agents/__pycache__/simple_course_agent.cpython-311.pyc and b/coursecrafter/agents/__pycache__/simple_course_agent.cpython-311.pyc differ
 
coursecrafter/agents/llm_client.py CHANGED
@@ -8,6 +8,7 @@ import json
8
  from typing import Dict, List, Any, Optional, AsyncGenerator
9
  from dataclasses import dataclass
10
  from abc import ABC, abstractmethod
 
11
 
12
  import openai
13
  import anthropic
@@ -249,6 +250,39 @@ class LlmClient:
249
  except Exception as e:
250
  print(f"❌ Failed to initialize {provider} client: {e}")
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  def get_available_providers(self) -> List[LLMProvider]:
253
  """Get list of available providers"""
254
  return list(self.clients.keys())
 
8
  from typing import Dict, List, Any, Optional, AsyncGenerator
9
  from dataclasses import dataclass
10
  from abc import ABC, abstractmethod
11
+ import os
12
 
13
  import openai
14
  import anthropic
 
250
  except Exception as e:
251
  print(f"❌ Failed to initialize {provider} client: {e}")
252
 
253
+ def update_provider_config(self, provider: str, api_key: str = None, **kwargs):
254
+ """Update configuration for a specific provider and reinitialize client"""
255
+
256
+ # Update environment variables
257
+ if provider == "openai" and api_key:
258
+ os.environ["OPENAI_API_KEY"] = api_key
259
+ elif provider == "anthropic" and api_key:
260
+ os.environ["ANTHROPIC_API_KEY"] = api_key
261
+ elif provider == "google" and api_key:
262
+ os.environ["GOOGLE_API_KEY"] = api_key
263
+ elif provider == "openai_compatible":
264
+ if api_key:
265
+ os.environ["OPENAI_COMPATIBLE_API_KEY"] = api_key
266
+ if kwargs.get("base_url"):
267
+ os.environ["OPENAI_COMPATIBLE_BASE_URL"] = kwargs["base_url"]
268
+ if kwargs.get("model"):
269
+ os.environ["OPENAI_COMPATIBLE_MODEL"] = kwargs["model"]
270
+
271
+ # Reinitialize the specific client
272
+ try:
273
+ if provider in ["openai", "openai_compatible"]:
274
+ self.clients[provider] = OpenAIClient(provider)
275
+ elif provider == "anthropic":
276
+ self.clients[provider] = AnthropicClient()
277
+ elif provider == "google":
278
+ self.clients[provider] = GoogleClient()
279
+
280
+ print(f"βœ… Updated and reinitialized {provider} client")
281
+ return True
282
+ except Exception as e:
283
+ print(f"❌ Failed to reinitialize {provider} client: {e}")
284
+ return False
285
+
286
  def get_available_providers(self) -> List[LLMProvider]:
287
  """Get list of available providers"""
288
  return list(self.clients.keys())
coursecrafter/agents/simple_course_agent.py CHANGED
@@ -106,6 +106,13 @@ For quizzes, return:
106
 
107
  Always strive to create courses that are not just informative, but are easy to understand, engaging, learning experiences."""
108
 
 
 
 
 
 
 
 
109
  async def generate_course(
110
  self,
111
  topic: str,
 
106
 
107
  Always strive to create courses that are not just informative, but are easy to understand, engaging, learning experiences."""
108
 
109
+ def update_provider_config(self, provider: str, api_key: str = None, **kwargs):
110
+ """Update provider configuration and reinitialize client"""
111
+ success = self.llm_client.update_provider_config(provider, api_key, **kwargs)
112
+ if success:
113
+ self.default_provider = provider
114
+ return success
115
+
116
  async def generate_course(
117
  self,
118
  topic: str,
coursecrafter/ui/__pycache__/gradio_app.cpython-311.pyc CHANGED
Binary files a/coursecrafter/ui/__pycache__/gradio_app.cpython-311.pyc and b/coursecrafter/ui/__pycache__/gradio_app.cpython-311.pyc differ
 
coursecrafter/ui/gradio_app.py CHANGED
@@ -775,8 +775,10 @@ def create_coursecrafter_interface() -> gr.Blocks:
775
 
776
  # Provider change handler to show/hide OpenAI-compatible fields
777
  def on_provider_change(provider):
778
- # Return visibility state for OpenAI-compatible fields
779
- return provider == "openai_compatible"
 
 
780
 
781
  # Event handlers
782
  async def generate_course_wrapper(topic: str, difficulty: str, lessons: int, provider: str, api_key: str, endpoint_url: str, model_name: str, progress=gr.Progress()):
@@ -785,28 +787,28 @@ def create_coursecrafter_interface() -> gr.Blocks:
785
  return (
786
  "<div class='error'>❌ Please enter a topic for your course.</div>",
787
  "", "",
788
- False, [], "<div class='image-details'>Error loading images</div>"
789
  )
790
 
791
  if not api_key.strip() and provider != "openai_compatible":
792
  return (
793
  "<div class='error'>❌ Please enter your API key for the selected LLM provider.</div>",
794
  "", "",
795
- False, [], "<div class='image-details'>Error loading images</div>"
796
  )
797
 
798
  if provider == "openai_compatible" and not endpoint_url.strip():
799
  return (
800
  "<div class='error'>❌ Please enter the endpoint URL for OpenAI-compatible provider.</div>",
801
  "", "",
802
- False, [], "<div class='image-details'>Error loading images</div>"
803
  )
804
 
805
  if provider == "openai_compatible" and not model_name.strip():
806
  return (
807
  "<div class='error'>❌ Please enter the model name for OpenAI-compatible provider.</div>",
808
  "", "",
809
- False, [], "<div class='image-details'>Error loading images</div>"
810
  )
811
 
812
  try:
@@ -827,13 +829,37 @@ def create_coursecrafter_interface() -> gr.Blocks:
827
  os.environ["OPENAI_COMPATIBLE_BASE_URL"] = endpoint_url
828
  os.environ["OPENAI_COMPATIBLE_MODEL"] = model_name
829
 
830
- # Initialize the simplified agent
 
831
  agent = SimpleCourseAgent()
832
- # Override the default provider with user selection
833
- agent.default_provider = provider
 
 
 
 
 
 
 
 
 
 
 
 
 
 
834
  course_context["agent"] = agent
835
  course_context["topic"] = topic
836
 
 
 
 
 
 
 
 
 
 
837
  progress(0.1, desc="βš™οΈ Setting up generation options...")
838
 
839
  # Create generation options
@@ -853,7 +879,7 @@ def create_coursecrafter_interface() -> gr.Blocks:
853
  return (
854
  "<div class='error'>❌ No LLM providers available. Please check your API keys.</div>",
855
  "", "",
856
- False, [], "<div class='image-details'>Error loading images</div>"
857
  )
858
 
859
  progress(0.2, desc="πŸŽ“ Starting course generation...")
@@ -953,8 +979,14 @@ def create_coursecrafter_interface() -> gr.Blocks:
953
  quiz_data = course_data.get("quiz", {})
954
  quizzes_html = format_quiz(quiz_data)
955
 
956
- # Show quiz button if quiz exists
957
- quiz_btn_visible = bool(quiz_data and quiz_data.get("questions"))
 
 
 
 
 
 
958
 
959
  progress(0.98, desc="πŸ–ΌοΈ Processing images for gallery...")
960
 
@@ -1210,7 +1242,7 @@ def create_coursecrafter_interface() -> gr.Blocks:
1210
 
1211
  return (
1212
  lessons_html, flashcards_html, quizzes_html,
1213
- quiz_btn_visible, images, image_details_html
1214
  )
1215
  else:
1216
  quiz_btn_visible = False
@@ -1218,7 +1250,7 @@ def create_coursecrafter_interface() -> gr.Blocks:
1218
 
1219
  return (
1220
  "", "", "",
1221
- quiz_btn_visible, [], "<div class='image-details'>No images available</div>"
1222
  )
1223
 
1224
  except Exception as e:
@@ -1227,13 +1259,13 @@ def create_coursecrafter_interface() -> gr.Blocks:
1227
  print(f"Error in course generation: {error_details}")
1228
  return (
1229
  "", "", "",
1230
- False, [], "<div class='image-details'>Error loading images</div>"
1231
  )
1232
 
1233
  def handle_quiz_submit():
1234
  """Handle quiz submission using client-side processing"""
1235
  # This function will be replaced by client-side JavaScript
1236
- return None
1237
 
1238
  async def handle_chat(message: str, current_chat: str):
1239
  """Handle chat messages for answering questions about the course content"""
 
775
 
776
  # Provider change handler to show/hide OpenAI-compatible fields
777
  def on_provider_change(provider):
778
+ if provider == "openai_compatible":
779
+ return gr.update(visible=True)
780
+ else:
781
+ return gr.update(visible=False)
782
 
783
  # Event handlers
784
  async def generate_course_wrapper(topic: str, difficulty: str, lessons: int, provider: str, api_key: str, endpoint_url: str, model_name: str, progress=gr.Progress()):
 
787
  return (
788
  "<div class='error'>❌ Please enter a topic for your course.</div>",
789
  "", "",
790
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
791
  )
792
 
793
  if not api_key.strip() and provider != "openai_compatible":
794
  return (
795
  "<div class='error'>❌ Please enter your API key for the selected LLM provider.</div>",
796
  "", "",
797
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
798
  )
799
 
800
  if provider == "openai_compatible" and not endpoint_url.strip():
801
  return (
802
  "<div class='error'>❌ Please enter the endpoint URL for OpenAI-compatible provider.</div>",
803
  "", "",
804
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
805
  )
806
 
807
  if provider == "openai_compatible" and not model_name.strip():
808
  return (
809
  "<div class='error'>❌ Please enter the model name for OpenAI-compatible provider.</div>",
810
  "", "",
811
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
812
  )
813
 
814
  try:
 
829
  os.environ["OPENAI_COMPATIBLE_BASE_URL"] = endpoint_url
830
  os.environ["OPENAI_COMPATIBLE_MODEL"] = model_name
831
 
832
+ # IMPORTANT: Create a fresh agent instance to pick up the new environment variables
833
+ # This ensures the LlmClient reinitializes with the updated API keys
834
  agent = SimpleCourseAgent()
835
+
836
+ # Use the new dynamic configuration method to update provider settings
837
+ config_kwargs = {}
838
+ if provider == "openai_compatible":
839
+ config_kwargs["base_url"] = endpoint_url
840
+ config_kwargs["model"] = model_name
841
+
842
+ # Update provider configuration dynamically
843
+ config_success = agent.update_provider_config(provider, api_key, **config_kwargs)
844
+ if not config_success:
845
+ return (
846
+ f"<div class='error'>❌ Failed to configure provider '{provider}'. Please check your API key and settings.</div>",
847
+ "", "",
848
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
849
+ )
850
+
851
  course_context["agent"] = agent
852
  course_context["topic"] = topic
853
 
854
+ # Verify the provider is available with the new configuration
855
+ available_providers = agent.get_available_providers()
856
+ if provider not in available_providers:
857
+ return (
858
+ f"<div class='error'>❌ Provider '{provider}' is not available after configuration. Please check your API key and configuration.</div>",
859
+ "", "",
860
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
861
+ )
862
+
863
  progress(0.1, desc="βš™οΈ Setting up generation options...")
864
 
865
  # Create generation options
 
879
  return (
880
  "<div class='error'>❌ No LLM providers available. Please check your API keys.</div>",
881
  "", "",
882
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
883
  )
884
 
885
  progress(0.2, desc="πŸŽ“ Starting course generation...")
 
979
  quiz_data = course_data.get("quiz", {})
980
  quizzes_html = format_quiz(quiz_data)
981
 
982
+ # Show quiz button if quiz exists - be more permissive to ensure it shows
983
+ quiz_btn_visible = bool(quiz_data and (quiz_data.get("questions") or len(str(quiz_data)) > 50))
984
+ print(f"🎯 Quiz button visibility: {quiz_btn_visible} (quiz_data: {bool(quiz_data)}, questions: {bool(quiz_data.get('questions') if quiz_data else False)})")
985
+
986
+ # Force quiz button to be visible if we have any quiz content
987
+ if quiz_data and not quiz_btn_visible:
988
+ print("⚠️ Forcing quiz button to be visible due to quiz data presence")
989
+ quiz_btn_visible = True
990
 
991
  progress(0.98, desc="πŸ–ΌοΈ Processing images for gallery...")
992
 
 
1242
 
1243
  return (
1244
  lessons_html, flashcards_html, quizzes_html,
1245
+ gr.update(visible=quiz_btn_visible), images, image_details_html
1246
  )
1247
  else:
1248
  quiz_btn_visible = False
 
1250
 
1251
  return (
1252
  "", "", "",
1253
+ gr.update(visible=quiz_btn_visible), [], "<div class='image-details'>No images available</div>"
1254
  )
1255
 
1256
  except Exception as e:
 
1259
  print(f"Error in course generation: {error_details}")
1260
  return (
1261
  "", "", "",
1262
+ gr.update(visible=False), [], "<div class='image-details'>Error loading images</div>"
1263
  )
1264
 
1265
  def handle_quiz_submit():
1266
  """Handle quiz submission using client-side processing"""
1267
  # This function will be replaced by client-side JavaScript
1268
+ return gr.update()
1269
 
1270
  async def handle_chat(message: str, current_chat: str):
1271
  """Handle chat messages for answering questions about the course content"""
coursecrafter/utils/__pycache__/config.cpython-311.pyc CHANGED
Binary files a/coursecrafter/utils/__pycache__/config.cpython-311.pyc and b/coursecrafter/utils/__pycache__/config.cpython-311.pyc differ
 
coursecrafter/utils/config.py CHANGED
@@ -163,6 +163,9 @@ class Config:
163
 
164
  def get_llm_config(self, provider: LLMProvider) -> LLMProviderConfig:
165
  """Get configuration for a specific LLM provider"""
 
 
 
166
  if provider not in self._config["llm_providers"]:
167
  raise ValueError(f"Unknown LLM provider: {provider}")
168
 
@@ -171,6 +174,9 @@ class Config:
171
 
172
  def get_available_llm_providers(self) -> List[LLMProvider]:
173
  """Get list of available LLM providers with API keys"""
 
 
 
174
  available = []
175
  for provider, config in self._config["llm_providers"].items():
176
  if provider == "openai_compatible":
@@ -185,6 +191,9 @@ class Config:
185
 
186
  def get_default_llm_provider(self) -> LLMProvider:
187
  """Get the default LLM provider, falling back to first available if not configured"""
 
 
 
188
  default_provider = self._config["system"]["default_llm_provider"]
189
  available_providers = self.get_available_llm_providers()
190
 
@@ -197,8 +206,9 @@ class Config:
197
  print(f"⚠️ Default provider '{default_provider}' not configured, using '{available_providers[0]}'")
198
  return available_providers[0]
199
 
200
- # If no providers are available, raise an error
201
- raise ValueError("No LLM providers are configured. Please set up at least one provider.")
 
202
 
203
  def get_image_generation_config(self) -> Dict[str, Any]:
204
  """Get image generation configuration"""
 
163
 
164
  def get_llm_config(self, provider: LLMProvider) -> LLMProviderConfig:
165
  """Get configuration for a specific LLM provider"""
166
+ # Reload config to pick up any environment variable changes
167
+ self._config = self._load_default_config()
168
+
169
  if provider not in self._config["llm_providers"]:
170
  raise ValueError(f"Unknown LLM provider: {provider}")
171
 
 
174
 
175
  def get_available_llm_providers(self) -> List[LLMProvider]:
176
  """Get list of available LLM providers with API keys"""
177
+ # Reload config to pick up any environment variable changes
178
+ self._config = self._load_default_config()
179
+
180
  available = []
181
  for provider, config in self._config["llm_providers"].items():
182
  if provider == "openai_compatible":
 
191
 
192
  def get_default_llm_provider(self) -> LLMProvider:
193
  """Get the default LLM provider, falling back to first available if not configured"""
194
+ # Reload config to pick up any environment variable changes
195
+ self._config = self._load_default_config()
196
+
197
  default_provider = self._config["system"]["default_llm_provider"]
198
  available_providers = self.get_available_llm_providers()
199
 
 
206
  print(f"⚠️ Default provider '{default_provider}' not configured, using '{available_providers[0]}'")
207
  return available_providers[0]
208
 
209
+ # If no providers are available, return a fallback instead of raising an error
210
+ print("⚠️ Warning: No LLM providers are configured. Returning 'google' as fallback.")
211
+ return "google" # Return a fallback provider that can be configured later
212
 
213
  def get_image_generation_config(self) -> Dict[str, Any]:
214
  """Get image generation configuration"""