Spaces:
Build error
Build error
jjz5463 commited on
Commit ·
15af633
1
Parent(s): 258c0f5
update gui
Browse files- app.py +66 -80
- chatbot_simulator.py +29 -30
- task_specific_data_population.py +5 -11
app.py
CHANGED
|
@@ -1,27 +1,27 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
from chatbot_simulator import ChatbotSimulation
|
| 3 |
from task_specific_data_population import DataPopulation
|
|
|
|
| 4 |
import os
|
| 5 |
|
| 6 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
| 7 |
|
| 8 |
-
|
| 9 |
class AppSimulator:
|
| 10 |
def __init__(self, openai_api_key):
|
| 11 |
self.simulation = None
|
| 12 |
-
self.conversation = [] # Stores full conversation for logging
|
| 13 |
-
self.display_conversation = [] # Stores last 2 messages for display
|
| 14 |
self.openai_api_key = openai_api_key
|
| 15 |
|
| 16 |
-
def initialize_simulator(self, task, app_name, sitemap):
|
| 17 |
-
"""Initialize the simulator with retries
|
| 18 |
-
success = False
|
| 19 |
retry_count = 0
|
| 20 |
max_retries = 50
|
| 21 |
|
| 22 |
-
while
|
| 23 |
try:
|
| 24 |
-
#
|
|
|
|
|
|
|
|
|
|
| 25 |
data_population = DataPopulation(api_key=self.openai_api_key)
|
| 26 |
sitemap_data, page_details, user_state = data_population.process_data(task, sitemap)
|
| 27 |
|
|
@@ -36,93 +36,79 @@ class AppSimulator:
|
|
| 36 |
agent='human'
|
| 37 |
)
|
| 38 |
|
| 39 |
-
#
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
return self.display_conversation
|
| 45 |
except Exception as e:
|
| 46 |
retry_count += 1
|
| 47 |
-
print(f"Attempt {retry_count}/{max_retries}
|
| 48 |
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
"""Handle one round of conversation."""
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
try:
|
| 55 |
-
# Get the response from the simulator
|
| 56 |
-
response = self.simulation.one_conversation_round(user_input)
|
| 57 |
-
|
| 58 |
-
# Log both user input and assistant's response
|
| 59 |
-
self._log_message("user", user_input)
|
| 60 |
-
self._log_message("assistant", response)
|
| 61 |
-
|
| 62 |
-
# Update display conversation (keep last 2 entries)
|
| 63 |
-
self.display_conversation.extend([(user_input, response)])
|
| 64 |
-
while len(self.display_conversation) > 2:
|
| 65 |
-
self.display_conversation.pop(0)
|
| 66 |
-
print(len(self.display_conversation))
|
| 67 |
-
return self.display_conversation
|
| 68 |
-
except Exception as e:
|
| 69 |
-
return [("system", f"An error occurred: {e}")]
|
| 70 |
-
|
| 71 |
-
def _log_message(self, role, content):
|
| 72 |
-
"""Log conversation messages to both memory and file."""
|
| 73 |
-
self.conversation.append({"role": role, "content": content})
|
| 74 |
-
self._write_log_to_file(content)
|
| 75 |
-
|
| 76 |
-
def _write_log_to_file(self, content):
|
| 77 |
-
"""Append conversation to a log file."""
|
| 78 |
-
log_location = self.simulation.log_location if self.simulation else "conversation_log.txt"
|
| 79 |
-
try:
|
| 80 |
-
with open(log_location, 'a') as f:
|
| 81 |
-
for message in self.conversation:
|
| 82 |
-
f.write(f"{message['role']}: {message['content']}\n\n")
|
| 83 |
-
except Exception as e:
|
| 84 |
-
print(f"Error logging conversation: {e}")
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
# JavaScript function to refresh theme to dark mode
|
| 88 |
-
js_func = """
|
| 89 |
-
function refresh() {
|
| 90 |
-
const url = new URL(window.location);
|
| 91 |
-
if (url.searchParams.get('__theme') !== 'dark') {
|
| 92 |
-
url.searchParams.set('__theme', 'dark');
|
| 93 |
-
window.location.href = url.href;
|
| 94 |
-
}
|
| 95 |
-
}
|
| 96 |
-
"""
|
| 97 |
|
|
|
|
| 98 |
simulator_app = AppSimulator(openai_api_key=openai_api_key)
|
| 99 |
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
gr.Markdown("## Simulator Setup")
|
| 103 |
|
|
|
|
| 104 |
task_input = gr.Textbox(label="Task", placeholder="Describe your task...")
|
| 105 |
app_name_input = gr.Textbox(label="App Name", placeholder="Enter the app name...")
|
| 106 |
sitemap_input = gr.Textbox(label="Sitemap", placeholder="Enter the Hugging Face link to sitemap...")
|
| 107 |
-
|
| 108 |
initialize_button = gr.Button("Initialize Simulator")
|
| 109 |
-
chatbot = gr.Chatbot(label="Simulator Chat", height=800)
|
| 110 |
-
user_message = gr.Textbox(label="Enter your message", placeholder="Type your message here...")
|
| 111 |
-
submit_button = gr.Button("Send")
|
| 112 |
|
| 113 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
initialize_button.click(
|
| 115 |
-
|
| 116 |
inputs=[task_input, app_name_input, sitemap_input],
|
| 117 |
-
outputs=
|
| 118 |
-
)
|
| 119 |
-
|
| 120 |
-
# Handle conversation interaction
|
| 121 |
-
submit_button.click(
|
| 122 |
-
simulator_app.chatbot_interaction,
|
| 123 |
-
inputs=user_message,
|
| 124 |
-
outputs=chatbot
|
| 125 |
)
|
| 126 |
|
| 127 |
-
# Launch the
|
| 128 |
-
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from chatbot_simulator import ChatbotSimulation
|
| 3 |
from task_specific_data_population import DataPopulation
|
| 4 |
+
import re
|
| 5 |
import os
|
| 6 |
|
| 7 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
| 8 |
|
|
|
|
| 9 |
class AppSimulator:
|
| 10 |
def __init__(self, openai_api_key):
|
| 11 |
self.simulation = None
|
|
|
|
|
|
|
| 12 |
self.openai_api_key = openai_api_key
|
| 13 |
|
| 14 |
+
def initialize_simulator(self, task, app_name, sitemap, progress=gr.Progress(track_tqdm=True)):
|
| 15 |
+
"""Initialize the simulator with retries and elapsed time tracking."""
|
|
|
|
| 16 |
retry_count = 0
|
| 17 |
max_retries = 50
|
| 18 |
|
| 19 |
+
while retry_count < max_retries:
|
| 20 |
try:
|
| 21 |
+
# Display current attempt with progress bar
|
| 22 |
+
progress.update(f"Initializing... Attempt {retry_count + 1}/{max_retries}")
|
| 23 |
+
|
| 24 |
+
# Process data and initialize the simulation
|
| 25 |
data_population = DataPopulation(api_key=self.openai_api_key)
|
| 26 |
sitemap_data, page_details, user_state = data_population.process_data(task, sitemap)
|
| 27 |
|
|
|
|
| 36 |
agent='human'
|
| 37 |
)
|
| 38 |
|
| 39 |
+
# Successful initialization
|
| 40 |
+
initial_message = self.simulation.start_conversation()
|
| 41 |
+
progress.update("Initialization Successful")
|
| 42 |
+
return initial_message # Return the initial assistant message for chat
|
|
|
|
|
|
|
| 43 |
except Exception as e:
|
| 44 |
retry_count += 1
|
| 45 |
+
print(f"Attempt {retry_count}/{max_retries} failed: {e}")
|
| 46 |
|
| 47 |
+
# If all retries failed
|
| 48 |
+
progress.update("Failed to initialize simulator after multiple retries.")
|
| 49 |
+
return "Initialization failed." # Error message for chat
|
| 50 |
+
|
| 51 |
+
def chat_interaction(self, user_input, history):
|
| 52 |
"""Handle one round of conversation."""
|
| 53 |
+
return self.simulation.one_conversation_round(user_input)
|
| 54 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
# Initialize the simulator
|
| 57 |
simulator_app = AppSimulator(openai_api_key=openai_api_key)
|
| 58 |
|
| 59 |
+
def is_valid_input(user_input):
|
| 60 |
+
"""Validate user input format."""
|
| 61 |
+
pattern = r"^\d+\.\s+.*"
|
| 62 |
+
return bool(re.match(pattern, user_input))
|
| 63 |
+
|
| 64 |
+
def chat(user_input, history):
|
| 65 |
+
"""Chat handler that validates input and interacts with the simulator."""
|
| 66 |
+
if is_valid_input(user_input):
|
| 67 |
+
valid_response = simulator_app.chat_interaction(user_input, history)
|
| 68 |
+
return valid_response
|
| 69 |
+
else:
|
| 70 |
+
invalid_response = "Invalid input. Please use the format: '<Number>. <Description>: query'"
|
| 71 |
+
return invalid_response
|
| 72 |
+
|
| 73 |
+
css = """
|
| 74 |
+
body {
|
| 75 |
+
background-color: black !important;
|
| 76 |
+
color: white;
|
| 77 |
+
}
|
| 78 |
+
.gradio-container {
|
| 79 |
+
background-color: black !important;
|
| 80 |
+
}
|
| 81 |
+
.chatbox-message, input {
|
| 82 |
+
color: white !important;
|
| 83 |
+
}
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
# Gradio Interface using ChatInterface
|
| 87 |
+
with gr.Blocks(fill_height=True, css=css) as demo:
|
| 88 |
gr.Markdown("## Simulator Setup")
|
| 89 |
|
| 90 |
+
# Input fields for initialization
|
| 91 |
task_input = gr.Textbox(label="Task", placeholder="Describe your task...")
|
| 92 |
app_name_input = gr.Textbox(label="App Name", placeholder="Enter the app name...")
|
| 93 |
sitemap_input = gr.Textbox(label="Sitemap", placeholder="Enter the Hugging Face link to sitemap...")
|
|
|
|
| 94 |
initialize_button = gr.Button("Initialize Simulator")
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
# Status block to display initialization progress with elapsed time
|
| 97 |
+
status = gr.Textbox(label="Status", interactive=False)
|
| 98 |
+
|
| 99 |
+
# Chat interface to handle user interactions
|
| 100 |
+
chat_interface = gr.ChatInterface(fn=chat)
|
| 101 |
+
|
| 102 |
+
# Define the callback function to initialize the simulator and update status
|
| 103 |
+
def initialize_and_start_chat(task, app_name, sitemap):
|
| 104 |
+
return simulator_app.initialize_simulator(task, app_name, sitemap) # Use progress tracking
|
| 105 |
+
|
| 106 |
+
# Set up the button click to initialize simulator and update status only
|
| 107 |
initialize_button.click(
|
| 108 |
+
fn=initialize_and_start_chat,
|
| 109 |
inputs=[task_input, app_name_input, sitemap_input],
|
| 110 |
+
outputs=status # Update only the status block
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
)
|
| 112 |
|
| 113 |
+
# Launch the app
|
| 114 |
+
demo.launch()
|
chatbot_simulator.py
CHANGED
|
@@ -59,12 +59,11 @@ You are at the {current_page} page. You have the following options:
|
|
| 59 |
3. Feature 3
|
| 60 |
4. Feature 4
|
| 61 |
|
| 62 |
-
Please enter your choice as
|
| 63 |
|
| 64 |
Rules:
|
| 65 |
- Be sure to display all options that is available in features.
|
| 66 |
- Be robotic and emotionless. Avoid offering any advice to the user.
|
| 67 |
-
- If a feature requires `input_text`, request input as: "Enter query as: [number]: query"
|
| 68 |
"""
|
| 69 |
|
| 70 |
def _get_openai_response(self, prompt):
|
|
@@ -74,7 +73,7 @@ Rules:
|
|
| 74 |
model="gpt-4",
|
| 75 |
messages=prompt,
|
| 76 |
max_tokens=self.buffer_tokens, # Adjusted max_tokens if needed
|
| 77 |
-
temperature=
|
| 78 |
)
|
| 79 |
return response.choices[0].message.content
|
| 80 |
|
|
@@ -84,8 +83,8 @@ Rules:
|
|
| 84 |
|
| 85 |
def _trim_conversation(self):
|
| 86 |
"""Trim the conversation to keep it within the token limit."""
|
| 87 |
-
while self._calculate_token_count(self.conversation) > self.max_tokens - self.buffer_tokens:
|
| 88 |
-
self.conversation.pop(0)
|
| 89 |
|
| 90 |
def one_conversation_round(self, user_input):
|
| 91 |
"""Conduct one round of conversation between the user and the assistant."""
|
|
@@ -95,26 +94,22 @@ Rules:
|
|
| 95 |
|
| 96 |
# Update user state using GPT's response
|
| 97 |
update_prompt = f"""
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
Sitemap: {self.sitemap}
|
| 103 |
|
| 104 |
Instructions:
|
| 105 |
1. If the 'current_page' has changed, update it to a page from the sitemap.
|
| 106 |
-
2. If the task is finished, update 'task_completed' to 1. Otherwise, leave it
|
| 107 |
3. If no updates are needed, return the user state exactly as provided, without modification.
|
| 108 |
-
4. Preserve the **exact JSON structure** and **format** of the provided user state.
|
| 109 |
-
5. The output **must be a single JSON dictionary** representing the updated user state—do not wrap it in a list.
|
| 110 |
-
6. Do not change any other fields unless explicitly required by the instructions.
|
| 111 |
|
| 112 |
Important:
|
| 113 |
- Ensure 'current_page' and 'task_completed' are keys in the returned dictionary.
|
| 114 |
-
- Return
|
| 115 |
-
- **AVOID OUTPUT A LIST**, must be JSON!
|
| 116 |
|
| 117 |
-
Example Format:
|
| 118 |
{{
|
| 119 |
'current_page': 'Home',
|
| 120 |
'task_completed': 0,
|
|
@@ -127,20 +122,24 @@ Example Format:
|
|
| 127 |
# Parse and update the user state
|
| 128 |
updated_state = json_repair.loads(updated_state)
|
| 129 |
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
Given the {updated_state}, reformat it into a proper JSON.
|
| 133 |
-
Make sure follow the format:
|
| 134 |
-
{{
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
}}
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
self.user_state = updated_state
|
| 146 |
|
|
|
|
| 59 |
3. Feature 3
|
| 60 |
4. Feature 4
|
| 61 |
|
| 62 |
+
Please enter your choice as 'Number. Description'. If you have a query, enter as 'Number. Description: query'
|
| 63 |
|
| 64 |
Rules:
|
| 65 |
- Be sure to display all options that is available in features.
|
| 66 |
- Be robotic and emotionless. Avoid offering any advice to the user.
|
|
|
|
| 67 |
"""
|
| 68 |
|
| 69 |
def _get_openai_response(self, prompt):
|
|
|
|
| 73 |
model="gpt-4",
|
| 74 |
messages=prompt,
|
| 75 |
max_tokens=self.buffer_tokens, # Adjusted max_tokens if needed
|
| 76 |
+
temperature=0,
|
| 77 |
)
|
| 78 |
return response.choices[0].message.content
|
| 79 |
|
|
|
|
| 83 |
|
| 84 |
def _trim_conversation(self):
|
| 85 |
"""Trim the conversation to keep it within the token limit."""
|
| 86 |
+
while self._calculate_token_count(self.conversation) >= (self.max_tokens - self.buffer_tokens * 4):
|
| 87 |
+
self.conversation.pop(0)
|
| 88 |
|
| 89 |
def one_conversation_round(self, user_input):
|
| 90 |
"""Conduct one round of conversation between the user and the assistant."""
|
|
|
|
| 94 |
|
| 95 |
# Update user state using GPT's response
|
| 96 |
update_prompt = f"""
|
| 97 |
+
If user takes action '{user_input}' on {self.user_state['current_page']} page, which page will they move it?
|
| 98 |
+
Recall user's task: {self.task}
|
| 99 |
+
Update the user_state dictionary based on user's last action:
|
| 100 |
+
Current user_state: {self.user_state}
|
| 101 |
Sitemap: {self.sitemap}
|
| 102 |
|
| 103 |
Instructions:
|
| 104 |
1. If the 'current_page' has changed, update it to a page from the sitemap.
|
| 105 |
+
2. If the task is finished, update 'task_completed' to 1. Otherwise, leave it as 0.
|
| 106 |
3. If no updates are needed, return the user state exactly as provided, without modification.
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
Important:
|
| 109 |
- Ensure 'current_page' and 'task_completed' are keys in the returned dictionary.
|
| 110 |
+
- Return only the dictionary without additional output or wrapping.
|
|
|
|
| 111 |
|
| 112 |
+
Example Output Format:
|
| 113 |
{{
|
| 114 |
'current_page': 'Home',
|
| 115 |
'task_completed': 0,
|
|
|
|
| 122 |
# Parse and update the user state
|
| 123 |
updated_state = json_repair.loads(updated_state)
|
| 124 |
|
| 125 |
+
# if isinstance(updated_state, list):
|
| 126 |
+
# reformat_prompt = f'''
|
| 127 |
+
# Given the {updated_state}, reformat it into a proper JSON.
|
| 128 |
+
# Make sure follow the format:
|
| 129 |
+
# {{
|
| 130 |
+
# 'current_page': 'Home',
|
| 131 |
+
# 'task_completed': 0,
|
| 132 |
+
# }}
|
| 133 |
+
# '''
|
| 134 |
+
# self.conversation.append({"role": "assistant", "content": reformat_prompt})
|
| 135 |
+
# reformat_state = self._get_openai_response(self.conversation)
|
| 136 |
+
# updated_state = json_repair.loads(reformat_state)
|
| 137 |
+
|
| 138 |
+
try:
|
| 139 |
+
if updated_state['task_completed']:
|
| 140 |
+
return f"Task completed! You took {self.prompt_count} steps."
|
| 141 |
+
except:
|
| 142 |
+
updated_state['task_completed'] = 0
|
| 143 |
|
| 144 |
self.user_state = updated_state
|
| 145 |
|
task_specific_data_population.py
CHANGED
|
@@ -17,15 +17,6 @@ class DataPopulation:
|
|
| 17 |
)
|
| 18 |
}
|
| 19 |
]
|
| 20 |
-
self.feature_update_conversation = [
|
| 21 |
-
{
|
| 22 |
-
"role": "system",
|
| 23 |
-
"content": (
|
| 24 |
-
"You are an intelligent assistant specialized in web page management tasks. "
|
| 25 |
-
"Your responsibilities is to identify which type of actions (select vs text_input) does each feature represents."
|
| 26 |
-
)
|
| 27 |
-
}
|
| 28 |
-
]
|
| 29 |
|
| 30 |
def fetch_huggingface_dataset(self, dataset_name):
|
| 31 |
"""Fetch the dataset from Hugging Face."""
|
|
@@ -37,7 +28,7 @@ class DataPopulation:
|
|
| 37 |
model="gpt-4",
|
| 38 |
messages=conversation,
|
| 39 |
max_tokens=1000, # Adjusted max_tokens if needed
|
| 40 |
-
temperature=
|
| 41 |
)
|
| 42 |
return response.choices[0].message.content.strip()
|
| 43 |
|
|
@@ -122,7 +113,10 @@ class DataPopulation:
|
|
| 122 |
self.conversation.append({"role": "assistant", "content": updated_user_data})
|
| 123 |
updated_user_data = json_repair.loads(updated_user_data)
|
| 124 |
for uid, page_data in updated_user_data.items():
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
# Step 5: Update user state
|
| 128 |
updated_user_state = self.ask_to_update_user_state(task, user_state)
|
|
|
|
| 17 |
)
|
| 18 |
}
|
| 19 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
def fetch_huggingface_dataset(self, dataset_name):
|
| 22 |
"""Fetch the dataset from Hugging Face."""
|
|
|
|
| 28 |
model="gpt-4",
|
| 29 |
messages=conversation,
|
| 30 |
max_tokens=1000, # Adjusted max_tokens if needed
|
| 31 |
+
temperature=0,
|
| 32 |
)
|
| 33 |
return response.choices[0].message.content.strip()
|
| 34 |
|
|
|
|
| 113 |
self.conversation.append({"role": "assistant", "content": updated_user_data})
|
| 114 |
updated_user_data = json_repair.loads(updated_user_data)
|
| 115 |
for uid, page_data in updated_user_data.items():
|
| 116 |
+
try:
|
| 117 |
+
page_details[uid]['user_data'] = page_data['user_data']
|
| 118 |
+
except:
|
| 119 |
+
continue
|
| 120 |
|
| 121 |
# Step 5: Update user state
|
| 122 |
updated_user_state = self.ask_to_update_user_state(task, user_state)
|