Severian commited on
Commit
77e7cfe
·
verified ·
1 Parent(s): cb9c25e

Update chatbot.py

Browse files
Files changed (1) hide show
  1. chatbot.py +182 -229
chatbot.py CHANGED
@@ -1,249 +1,202 @@
1
- import gradio as gr
2
- from sqlalchemy.exc import SQLAlchemyError
3
- from utils import InnovativeIdea, init_db, get_db, SessionLocal, get_llm_response
4
- from data_models import IdeaForm
5
- from chatbot import InnovativeIdeaChatbot
6
- from config import MODELS, DEFAULT_SYSTEM_PROMPT, STAGES
7
- import logging
8
- import os
9
- from typing import Dict, Any
10
-
11
- # Set up logging
12
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
-
14
- def create_gradio_interface():
15
- innovative_chatbot = InnovativeIdeaChatbot()
16
- default_stage = STAGES[0]["name"]
17
-
18
- # Initialize the database
19
- try:
20
  init_db()
21
- db = next(get_db())
22
- initial_idea = db.query(InnovativeIdea).first()
23
- if initial_idea is None:
24
- logging.info("No initial idea found in the database. Creating a new one.")
25
- initial_idea = InnovativeIdea()
26
- db.add(initial_idea)
27
- db.commit()
28
- db.refresh(initial_idea)
29
-
30
- # Create form_fields while the session is still open
31
- form_fields = {
32
- stage["name"]: gr.Textbox(
33
- label=stage["question"],
34
- placeholder=stage["example"],
35
- value=getattr(initial_idea, stage["field"], ""),
36
- visible=(stage["name"] == default_stage),
37
- interactive=False
38
- ) for stage in STAGES
39
- }
40
-
41
- # Now we can safely close the session
42
- db.close()
43
- except SQLAlchemyError as e:
44
- logging.error(f"Database initialization failed: {str(e)}")
45
- raise RuntimeError(f"Failed to initialize database: {str(e)}")
46
-
47
- def chatbot_function(message, history, model, system_prompt, thinking_budget, current_stage):
48
- try:
49
- # If this is the first message, get the initial greeting
50
- if not history:
51
- initial_greeting = innovative_chatbot.get_initial_greeting()
52
- history.append((None, initial_greeting))
53
- return history, "", ""
54
-
55
- chat_history, form_data = innovative_chatbot.process_stage_input(current_stage, message, model, system_prompt, thinking_budget)
56
-
57
- history.append((message, chat_history[-1][1]))
58
-
59
- # Update the database with the new form data
60
- db = SessionLocal()
61
- idea = db.query(InnovativeIdea).first()
62
- for key, value in form_data.items():
63
- setattr(idea, key, value)
64
- db.commit()
65
- db.close()
66
-
67
- return history, form_data.get(current_stage, ""), "" # Return an empty string to clear the input
68
- except Exception as e:
69
- logging.error(f"An error occurred in chatbot_function: {str(e)}", exc_info=True)
70
- return history + [(None, f"An error occurred: {str(e)}")], "", "" # Return an empty string to clear the input
71
-
72
- def fill_form(stage, model, thinking_budget):
73
- form_data = innovative_chatbot.fill_out_form(stage, model, thinking_budget)
74
- return form_data
75
-
76
- def clear_chat():
77
- # Reset the database to an empty form
78
- db = SessionLocal()
79
- idea = db.query(InnovativeIdea).first()
80
- empty_form = IdeaForm()
81
- for key, value in empty_form.dict().items():
82
- setattr(idea, key, value)
83
- db.commit()
84
- db.close()
85
-
86
- return innovative_chatbot.reset()
87
-
88
- def update_form_field(stage_name: str, value: str) -> str:
89
- db = SessionLocal()
90
- idea = db.query(InnovativeIdea).first()
91
- field_name = next(stage["field"] for stage in STAGES if stage["name"] == stage_name)
92
- setattr(idea, field_name, value)
93
- db.commit()
94
- db.close()
95
- return value
96
-
97
- def update_placeholders(form_data: Dict[str, str]) -> Dict[str, Any]:
98
- return {
99
- stage["name"]: gr.update(value=form_data.get(stage["name"], ""), placeholder=form_data.get(stage["name"], stage["example"]))
100
- for stage in STAGES
101
- }
102
-
103
- def start_over():
104
- chat_history, form_data = innovative_chatbot.start_over()
105
- return (
106
- [], # Clear the chatbot
107
- "", # Clear the message input
108
- *[form_data.get(stage["field"], "") for stage in STAGES], # Reset all form fields
109
- gr.update(value=default_stage) # Reset the stage selection
110
  )
111
 
112
- def fill_and_update_form(stage, model, thinking_budget):
113
- # Get the conversation history
114
- chat_history = innovative_chatbot.chat_history
115
-
116
- # Get the specific question for the current stage
117
- stage_info = next((s for s in STAGES if s["name"] == stage), None)
118
- if not stage_info:
119
- return [gr.update() for _ in STAGES] # Return no updates if stage not found
120
-
121
- stage_question = stage_info["question"]
122
-
123
- # Use the generate_form_data method from InnovativeIdeaChatbot
124
- form_data = innovative_chatbot.generate_form_data(stage, model, thinking_budget)
125
-
126
- # Update the database and form fields
127
- updated_fields = {}
128
- updated_value = update_form_field(stage, form_data)
129
- updated_fields[stage] = updated_value
130
-
131
- # Convert the dictionary to a list of individual updates
132
- return [gr.update(value=updated_fields.get(s["name"], ""),
133
- placeholder=updated_fields.get(s["name"], s["example"]))
134
- for s in STAGES]
135
 
136
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
137
- gr.Markdown("# Innovative Idea Generator")
 
 
 
 
 
 
 
138
 
139
- mode = gr.Radio(["Chatbot", "Direct Input"], label="Mode", value="Chatbot")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
- with gr.Row():
142
- with gr.Column(scale=2):
143
- chatbot = gr.Chatbot(label="Conversation", height=500)
144
- msg = gr.Textbox(label="Your input", placeholder="Type your brilliant idea here...")
145
-
146
- with gr.Row():
147
- submit = gr.Button("Submit")
148
- clear = gr.Button("Clear Chat")
149
- start_over_btn = gr.Button("Start Over")
150
-
151
- with gr.Column(scale=1):
152
- stages = gr.Radio(
153
- choices=[stage["name"] for stage in STAGES],
154
- label="Ideation Stages",
155
- value=default_stage
156
- )
157
- form_fields = {
158
- stage["name"]: gr.Textbox(
159
- label=stage["question"],
160
- placeholder=stage["example"],
161
- value=getattr(initial_idea, stage["field"], ""),
162
- visible=(stage["name"] == default_stage),
163
- interactive=False
164
- ) for stage in STAGES
165
- }
166
- fill_form_btn = gr.Button("Fill out Form")
167
- submit_form_btn = gr.Button("Submit Form", visible=False)
168
-
169
- with gr.Accordion("Advanced Settings", open=False):
170
- model = gr.Dropdown(choices=MODELS, label="Select Model", value=MODELS[0])
171
- system_prompt = gr.Textbox(label="System Prompt", value=DEFAULT_SYSTEM_PROMPT, lines=5)
172
- thinking_budget = gr.Slider(minimum=1, maximum=4098, value=2048, step=1, label="Max New Tokens")
173
- api_key = gr.Textbox(label="Hugging Face API Key", type="password")
174
-
175
- # Event handlers
176
- msg.submit(chatbot_function,
177
- inputs=[msg, chatbot, model, system_prompt, thinking_budget, stages],
178
- outputs=[chatbot, form_fields[default_stage], msg])
179
 
180
- submit.click(chatbot_function,
181
- inputs=[msg, chatbot, model, system_prompt, thinking_budget, stages],
182
- outputs=[chatbot, form_fields[default_stage], msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- fill_form_btn.click(fill_and_update_form,
185
- inputs=[stages, model, thinking_budget],
186
- outputs=list(form_fields.values()))
187
 
188
- clear.click(clear_chat,
189
- outputs=[chatbot] + list(form_fields.values()))
190
-
191
- # Update form field visibility based on selected stage
192
- stages.change(
193
- lambda s: [gr.update(visible=(stage["name"] == s)) for stage in STAGES],
194
- inputs=[stages],
195
- outputs=list(form_fields.values())
196
  )
197
 
198
- # Update API key when changed
199
- api_key.change(innovative_chatbot.set_api_key, inputs=[api_key])
200
 
201
- # Toggle between chatbot and direct input mode
202
- def toggle_mode(new_mode):
203
- if new_mode == "Direct Input":
204
- return [gr.update(visible=False)] * 3 + [gr.update(interactive=True)] * len(STAGES) + [gr.update(visible=True)]
205
- else:
206
- return [gr.update(visible=True)] * 3 + [gr.update(interactive=False)] * len(STAGES) + [gr.update(visible=False)]
207
 
208
- mode.change(
209
- toggle_mode,
210
- inputs=[mode],
211
- outputs=[chatbot, msg, submit] + list(form_fields.values()) + [submit_form_btn]
212
- )
213
 
214
- # Handle direct form submission
215
- submit_form_btn.click(
216
- lambda *values: values,
217
- inputs=[form_fields[stage["name"]] for stage in STAGES],
218
- outputs=[form_fields[stage["name"]] for stage in STAGES]
219
- )
220
 
221
- # Add this new event handler for the Start Over button
222
- start_over_btn.click(
223
- start_over,
224
- outputs=[chatbot, msg] + [form_fields[stage["name"]] for stage in STAGES] + [stages]
225
- )
 
226
 
227
- # Add this new event handler to display the initial greeting when the interface loads
228
- demo.load(lambda: ([[None, innovative_chatbot.get_initial_greeting()]], ""),
229
- outputs=[chatbot, msg])
230
 
231
- return demo
 
 
 
232
 
233
- def main():
234
- try:
235
- demo = create_gradio_interface()
236
- return demo
237
- except Exception as e:
238
- logging.error(f"Failed to initialize application: {str(e)}", exc_info=True)
239
- raise
 
 
 
 
 
 
 
 
 
240
 
 
241
  if __name__ == "__main__":
242
- try:
243
- demo = main()
244
- demo.launch()
245
- except Exception as e:
246
- logging.error(f"Failed to start the application: {str(e)}", exc_info=True)
247
- print(f"An error occurred while starting the application: {str(e)}")
248
- print("Please check the log file for more details.")
249
- # You might want to add a more user-friendly error message or UI here
 
1
+ from typing import List, Tuple, Dict, Any, Optional
2
+ from data_models import IdeaForm, IDEA_STAGES
3
+ from config import DEFAULT_SYSTEM_PROMPT, STAGES
4
+ from utils import (
5
+ get_llm_response, extract_form_data, save_idea_to_database,
6
+ load_idea_from_database, update_idea_in_database,
7
+ get_db, clear_database, init_db, create_tables,
8
+ perform_web_search, optimize_search_query,
9
+ SessionLocal, InnovativeIdea
10
+ )
11
+
12
+ class InnovativeIdeaChatbot:
13
+ def __init__(self):
14
+ create_tables()
 
 
 
 
 
15
  init_db()
16
+ self.idea_form = IdeaForm()
17
+ self.chat_history = []
18
+ self.idea_id = None
19
+ self.db = SessionLocal()
20
+ self.current_stage = None
21
+ self.api_key = None
22
+ self.add_system_message(self.get_initial_greeting())
23
+
24
+ def __del__(self):
25
+ if hasattr(self, 'db'):
26
+ self.db.close()
27
+
28
+ def get_initial_greeting(self) -> str:
29
+ greeting = """
30
+ Welcome to the Innovative Idea Generator! I'm Myamoto, your AI assistant designed to help you refine and develop your innovative ideas.
31
+ Here's how we'll work together:
32
+ 1. We'll go through 10 stages to explore different aspects of your idea.
33
+ 2. At each stage, I'll ask you questions and provide feedback to help you think deeper about your concept.
34
+ 3. You can ask me questions at any time or request more information on a topic.
35
+ 4. If you want to perform a web search for additional information, just start your message with '@' followed by your search query.
36
+ 5. When you're ready to move to the next stage, simply type 'next'.
37
+ Let's start by exploring your innovative idea! What's the name of your idea, or would you like help coming up with one?
38
+ """
39
+ self.greeted = True
40
+ return greeting
41
+
42
+ def add_system_message(self, message: str):
43
+ self.chat_history.append(("System", message))
44
+
45
+ def set_api_key(self, api_key: str):
46
+ self.api_key = api_key
47
+
48
+ def activate_stage(self, stage_name: str) -> Optional[str]:
49
+ self.current_stage = stage_name
50
+ for stage in STAGES:
51
+ if stage["name"] == stage_name:
52
+ return f"Let's work on the '{stage_name}' stage. {stage['question']}"
53
+ return None
54
+
55
+ def process_stage_input(self, stage_name: str, message: str, model: str, system_prompt: str, thinking_budget: int) -> Tuple[List[Tuple[str, str]], Dict[str, Any]]:
56
+ if self.current_stage != stage_name:
57
+ activation_message = self.activate_stage(stage_name)
58
+ if activation_message is None:
59
+ error_message = f"Error: Unable to activate stage '{stage_name}'. Please check if the stage name is correct."
60
+ self.chat_history.append(("System", error_message))
61
+ return self.chat_history, self.idea_form.dict()
62
+ self.chat_history.append(("System", activation_message))
63
+
64
+ # Check for web search request
65
+ if message.startswith('@'):
66
+ search_query = message[1:].strip()
67
+ optimized_query = optimize_search_query(search_query, model)
68
+ search_results = perform_web_search(optimized_query)
69
+ self.chat_history.append(("Human", message))
70
+ self.chat_history.append(("AI", f"Here are the search results for '{optimized_query}':\n\n{search_results}"))
71
+ return self.chat_history, self.idea_form.dict()
72
+
73
+ # Generate the prompt for the current stage
74
+ stage_prompt = self.generate_prompt_for_stage(stage_name)
75
+
76
+ # Use the DEFAULT_SYSTEM_PROMPT from config.py
77
+ formatted_system_prompt = DEFAULT_SYSTEM_PROMPT.format(
78
+ current_stage=stage_name,
79
+ stage_prompt=stage_prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  )
81
 
82
+ # Combine the formatted system prompt and user's input
83
+ combined_prompt = f"{formatted_system_prompt}\n\nUser input: {message}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ # Get LLM response
86
+ llm_response = get_llm_response(combined_prompt, model, thinking_budget, self.api_key)
87
+
88
+ # Add the interaction to chat history
89
+ self.chat_history.append(("Human", message))
90
+ self.chat_history.append(("AI", llm_response))
91
+
92
+ # Extract form data from the LLM response
93
+ form_data = extract_form_data(llm_response)
94
 
95
+ # Update the idea form
96
+ if stage_name in form_data:
97
+ setattr(self.idea_form, stage_name.lower().replace(" ", "_"), form_data[stage_name])
98
+
99
+ return self.chat_history, self.idea_form.dict()
100
+
101
+ def fill_out_form(self, current_stage: str, model: str, thinking_budget: int) -> Dict[str, str]:
102
+ form_data = {}
103
+ for stage in IDEA_STAGES:
104
+ stage_name = stage.name
105
+ if stage_name == current_stage:
106
+ # Generate new data for the current stage
107
+ form_data[stage_name] = self.generate_form_data(stage_name, model, thinking_budget)
108
+ else:
109
+ # Use existing data for other stages
110
+ form_data[stage_name] = getattr(self.idea_form, stage.field, "")
111
 
112
+ # Update the idea form
113
+ for stage in IDEA_STAGES:
114
+ setattr(self.idea_form, stage.field, form_data[stage.name])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ # Save to database
117
+ if self.idea_id:
118
+ update_idea_in_database(self.idea_id, self.idea_form, self.db)
119
+ else:
120
+ self.idea_id = save_idea_to_database(self.idea_form, self.db)
121
+
122
+ return form_data
123
+
124
+ def generate_prompt_for_stage(self, stage: str) -> str:
125
+ for s in IDEA_STAGES:
126
+ if s.name == stage:
127
+ return f"We are currently working on the '{stage}' stage. {s.question}"
128
+ return f"We are currently working on the '{stage}' stage. Please provide relevant information."
129
+
130
+ def reset(self):
131
+ self.chat_history = []
132
+ self.idea_form = IdeaForm()
133
+ self.idea_id = None
134
+ self.current_stage = None
135
+ self.add_system_message(self.get_initial_greeting())
136
+ clear_database(self.db)
137
+ return self.chat_history, self.idea_form.dict()
138
+
139
+ def update_idea_form(self, stage_name: str, form_data: str):
140
+ setattr(self.idea_form, stage_name.lower().replace(" ", "_"), form_data)
141
+ if self.idea_id:
142
+ update_idea_in_database(self.idea_id, self.idea_form, self.db)
143
+ else:
144
+ self.idea_id = save_idea_to_database(self.idea_form, self.db)
145
+
146
+ def generate_form_data(self, stage: str, model: str, thinking_budget: int) -> str:
147
+ # Prepare the conversation history for the LLM
148
+ conversation = "\n".join([f"{role}: {message}" for role, message in self.chat_history])
149
 
150
+ stage_prompt = self.generate_prompt_for_stage(stage)
 
 
151
 
152
+ formatted_system_prompt = DEFAULT_SYSTEM_PROMPT.format(
153
+ current_stage=stage,
154
+ stage_prompt=stage_prompt
 
 
 
 
 
155
  )
156
 
157
+ prompt = f"""
158
+ {formatted_system_prompt}
159
 
160
+ Based on the following conversation, extract the relevant information for the '{stage}' stage of the innovative idea:
 
 
 
 
 
161
 
162
+ {conversation}
 
 
 
 
163
 
164
+ Please provide a concise summary for the '{stage}' stage, focusing only on the information relevant to this stage.
165
+ Your response should be structured as follows:
 
 
 
 
166
 
167
+ 1. A brief analysis of the conversation related to this stage.
168
+ 2. A concise summary of the key points relevant to this stage.
169
+ 3. A suggested form entry for this stage, enclosed in <form_data></form_data> tags.
170
+
171
+ Remember to keep the form entry concise and directly related to the '{stage}' stage. Do not include information from other stages in the form entry.
172
+ """
173
 
174
+ # Get LLM response
175
+ llm_response = get_llm_response(prompt, model, thinking_budget, self.api_key)
 
176
 
177
+ # Extract form data from the LLM response
178
+ form_data = extract_form_data(llm_response)
179
+
180
+ return form_data.get(stage, "")
181
 
182
+ def start_over(self):
183
+ self.reset()
184
+ clear_database(self.db)
185
+ self.idea_id = None
186
+
187
+ # Reinitialize with a new empty idea
188
+ new_idea = InnovativeIdea()
189
+ self.db.add(new_idea)
190
+ self.db.commit()
191
+ self.idea_id = new_idea.id
192
+
193
+ self.idea_form = IdeaForm()
194
+ self.chat_history = []
195
+ self.add_system_message(self.get_initial_greeting())
196
+
197
+ return self.chat_history, self.idea_form.dict()
198
 
199
+ # Ensure this is at the end of the file
200
  if __name__ == "__main__":
201
+ # Any test code or standalone functionality for chatbot.py
202
+ pass