Prof-Reza commited on
Commit
9047f8a
·
verified ·
1 Parent(s): 9098a8c

Fix chat interface (return message dictionaries for Gradio Chatbot) and add fallback to COURSECREATOR_API_KEY for OpenAI API key retrieval.

Browse files
Files changed (2) hide show
  1. app.py +39 -18
  2. planner.py +41 -13
app.py CHANGED
@@ -16,26 +16,44 @@ SYSTEM_PROMPT = (
16
 
17
  def chat(user_message, chat_history, chat_pairs, sources, plan):
18
  """Handle a user chat message and return updated chat state."""
19
- # Ensure OpenAI API key is set
20
- openai.api_key = os.getenv("OPENAI_API_KEY")
21
- # initialise state variables if they are None
22
  if chat_history is None:
23
  chat_history = []
24
  if chat_pairs is None:
25
  chat_pairs = []
26
- # append the user's message to the conversation history
27
  chat_history.append({"role": "user", "content": user_message})
28
- # build messages including system prompt
29
  messages = [{"role": "system", "content": SYSTEM_PROMPT}] + chat_history
30
- # call OpenAI's ChatCompletion to get assistant's reply
31
  try:
32
- response = openai.ChatCompletion.create(
33
- model=os.getenv("OPENAI_MODEL", "gpt-5-mini"),
34
- messages=messages,
35
- temperature=float(os.getenv("TEMPERATURE", "0.7")),
36
- max_tokens=int(os.getenv("MAX_OUTPUT_TOKENS", "1024")),
37
- )
38
- assistant_reply = response["choices"][0]["message"]["content"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  except Exception as e:
40
  # When the API call fails (e.g. missing API key), return an error message
41
  assistant_reply = (
@@ -43,11 +61,12 @@ def chat(user_message, chat_history, chat_pairs, sources, plan):
43
  "Please ensure your OpenAI API key is configured in the Space secrets.\n"
44
  f"(Error: {e})"
45
  )
46
- # append assistant reply to conversation history
47
  chat_history.append({"role": "assistant", "content": assistant_reply})
48
- # append pair to display history for Chatbot component
49
  chat_pairs.append((user_message, assistant_reply))
50
- return chat_pairs, chat_history, chat_pairs, sources, plan
 
51
 
52
  def run_search(query, chat_history, chat_pairs, sources, plan, num_results=5, domain_filter=""):
53
  """Execute a web search and update sources list."""
@@ -119,7 +138,8 @@ with gr.Blocks() as demo:
119
  Chat with the assistant to brainstorm your course idea. Use web search to collect resources. When you're ready, click **Finalize Outline** to generate a course plan. Then generate the final course package (ZIP)."""
120
  )
121
  # Chat interface components
122
- # Use 'messages' type for Chatbot to avoid deprecation of tuple format
 
123
  chatbot = gr.Chatbot(label="Conversation", type="messages")
124
  msg_input = gr.Textbox(label="Your message", placeholder="Type your message and press Enter", lines=1)
125
  # Search controls
@@ -133,7 +153,7 @@ Chat with the assistant to brainstorm your course idea. Use web search to collec
133
  plan_output = gr.Textbox(label="Course outline", interactive=False)
134
  generate_btn = gr.Button("Generate Course Package")
135
  file_output = gr.File(label="course.zip")
136
- # State variables to keep track of conversation, display pairs, sources and plan
137
  state_chat_history = gr.State([])
138
  state_chat_pairs = gr.State([])
139
  state_sources = gr.State([])
@@ -142,6 +162,7 @@ Chat with the assistant to brainstorm your course idea. Use web search to collec
142
  msg_input.submit(
143
  chat,
144
  inputs=[msg_input, state_chat_history, state_chat_pairs, state_sources, state_plan],
 
145
  outputs=[chatbot, state_chat_history, state_chat_pairs, state_sources, state_plan],
146
  )
147
  # Handle search submission: update search results and sources; maintain other states unchanged
 
16
 
17
  def chat(user_message, chat_history, chat_pairs, sources, plan):
18
  """Handle a user chat message and return updated chat state."""
19
+ # Ensure lists are initialised
 
 
20
  if chat_history is None:
21
  chat_history = []
22
  if chat_pairs is None:
23
  chat_pairs = []
24
+ # Append the user's message to the conversation history (list of dictionaries for Chatbot)
25
  chat_history.append({"role": "user", "content": user_message})
26
+ # Build messages including system prompt for API call
27
  messages = [{"role": "system", "content": SYSTEM_PROMPT}] + chat_history
28
+ # Call OpenAI's ChatCompletion to get assistant's reply
29
  try:
30
+ model = os.getenv("OPENAI_MODEL", "gpt-5-mini")
31
+ temperature = float(os.getenv("TEMPERATURE", "0.7"))
32
+ max_tokens = int(os.getenv("MAX_OUTPUT_TOKENS", "1024"))
33
+ # Support alternative secret name COURSECREATOR_API_KEY as a fallback for the OpenAI API key
34
+ api_key = os.getenv("OPENAI_API_KEY") or os.getenv("COURSECREATOR_API_KEY")
35
+ if not api_key:
36
+ raise ValueError("OPENAI_API_KEY or COURSECREATOR_API_KEY is not set")
37
+ # Prefer the new OpenAI SDK (>=1.0) if available
38
+ if hasattr(openai, "OpenAI"):
39
+ client = openai.OpenAI(api_key=api_key)
40
+ response = client.chat.completions.create(
41
+ model=model,
42
+ messages=messages,
43
+ temperature=temperature,
44
+ max_tokens=max_tokens,
45
+ )
46
+ assistant_reply = response.choices[0].message.content
47
+ else:
48
+ # Legacy OpenAI SDK (<1.0)
49
+ openai.api_key = api_key
50
+ response = openai.ChatCompletion.create(
51
+ model=model,
52
+ messages=messages,
53
+ temperature=temperature,
54
+ max_tokens=max_tokens,
55
+ )
56
+ assistant_reply = response["choices"][0]["message"]["content"]
57
  except Exception as e:
58
  # When the API call fails (e.g. missing API key), return an error message
59
  assistant_reply = (
 
61
  "Please ensure your OpenAI API key is configured in the Space secrets.\n"
62
  f"(Error: {e})"
63
  )
64
+ # Append assistant reply to conversation history
65
  chat_history.append({"role": "assistant", "content": assistant_reply})
66
+ # Append pair to display history for any other uses (kept for compatibility)
67
  chat_pairs.append((user_message, assistant_reply))
68
+ # For Chatbot with type="messages", return the chat_history as the first output
69
+ return chat_history, chat_history, chat_pairs, sources, plan
70
 
71
  def run_search(query, chat_history, chat_pairs, sources, plan, num_results=5, domain_filter=""):
72
  """Execute a web search and update sources list."""
 
138
  Chat with the assistant to brainstorm your course idea. Use web search to collect resources. When you're ready, click **Finalize Outline** to generate a course plan. Then generate the final course package (ZIP)."""
139
  )
140
  # Chat interface components
141
+ # Use 'messages' type for Chatbot. The Chatbot expects a list of message dictionaries
142
+ # (e.g. {"role": "user", "content": "..."}) when type="messages".
143
  chatbot = gr.Chatbot(label="Conversation", type="messages")
144
  msg_input = gr.Textbox(label="Your message", placeholder="Type your message and press Enter", lines=1)
145
  # Search controls
 
153
  plan_output = gr.Textbox(label="Course outline", interactive=False)
154
  generate_btn = gr.Button("Generate Course Package")
155
  file_output = gr.File(label="course.zip")
156
+ # State variables to keep track of conversation (messages), display pairs, sources and plan
157
  state_chat_history = gr.State([])
158
  state_chat_pairs = gr.State([])
159
  state_sources = gr.State([])
 
162
  msg_input.submit(
163
  chat,
164
  inputs=[msg_input, state_chat_history, state_chat_pairs, state_sources, state_plan],
165
+ # First output goes to the Chatbot; we return the updated chat history (list of messages)
166
  outputs=[chatbot, state_chat_history, state_chat_pairs, state_sources, state_plan],
167
  )
168
  # Handle search submission: update search results and sources; maintain other states unchanged
planner.py CHANGED
@@ -3,19 +3,47 @@ import openai
3
 
4
 
5
  def plan_course(messages, sources):
6
- """Use OpenAI to plan a course based on messages and sources."""
7
- openai.api_key = os.getenv("OPENAI_API_KEY")
8
- system_prompt = "You are an expert course planner. Use the conversation and sources to propose a structured plan."
 
 
 
 
 
 
 
 
9
  formatted_messages = [{"role": "system", "content": system_prompt}]
10
- # append conversation messages
11
  for msg in messages:
12
  formatted_messages.append(msg)
13
- # simple call to OpenAI chat completion
14
- response = openai.ChatCompletion.create(
15
- model=os.getenv("OPENAI_MODEL", "gpt-5-mini"),
16
- messages=formatted_messages,
17
- temperature=float(os.getenv("TEMPERATURE", "0.7")),
18
- max_tokens=int(os.getenv("MAX_OUTPUT_TOKENS", "2048")),
19
- )
20
- plan_text = response["choices"][0]["message"]["content"]
21
- return plan_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
 
5
  def plan_course(messages, sources):
6
+ """Use OpenAI to plan a course based on messages and sources. Tries to handle different OpenAI SDK versions."""
7
+ # Ensure API key is available
8
+ # Support alternative secret name COURSECREATOR_API_KEY as a fallback for the OpenAI API key
9
+ api_key = os.getenv("OPENAI_API_KEY") or os.getenv("COURSECREATOR_API_KEY")
10
+ if not api_key:
11
+ raise ValueError(
12
+ "An OpenAI API key is required to plan the course (set OPENAI_API_KEY or COURSECREATOR_API_KEY)"
13
+ )
14
+ system_prompt = (
15
+ "You are an expert course planner. Use the conversation and sources to propose a structured plan."
16
+ )
17
  formatted_messages = [{"role": "system", "content": system_prompt}]
 
18
  for msg in messages:
19
  formatted_messages.append(msg)
20
+ model = os.getenv("OPENAI_MODEL", "gpt-5-mini")
21
+ temperature = float(os.getenv("TEMPERATURE", "0.7"))
22
+ max_tokens = int(os.getenv("MAX_OUTPUT_TOKENS", "2048"))
23
+ # Try to call OpenAI using v1-style client if available
24
+ try:
25
+ # Newer OpenAI Python SDK (>=1.0) exposes `OpenAI` client
26
+ if hasattr(openai, "OpenAI"):
27
+ client = openai.OpenAI(api_key=api_key)
28
+ response = client.chat.completions.create(
29
+ model=model,
30
+ messages=formatted_messages,
31
+ temperature=temperature,
32
+ max_tokens=max_tokens,
33
+ )
34
+ # response.choices is a list of objects
35
+ plan_text = response.choices[0].message.content
36
+ else:
37
+ # Legacy OpenAI SDK (<1.0)
38
+ openai.api_key = api_key
39
+ response = openai.ChatCompletion.create(
40
+ model=model,
41
+ messages=formatted_messages,
42
+ temperature=temperature,
43
+ max_tokens=max_tokens,
44
+ )
45
+ plan_text = response["choices"][0]["message"]["content"]
46
+ except Exception as e:
47
+ # Propagate error for caller to handle
48
+ raise RuntimeError(f"OpenAI API error: {e}")
49
+ return plan_text