jjz5463 commited on
Commit
c634ddd
1 Parent(s): 23cf2c3

update app simulator

Browse files
Files changed (3) hide show
  1. app.py +47 -52
  2. chatbot_simulator.py +125 -79
  3. prompts.py +89 -0
app.py CHANGED
@@ -1,8 +1,9 @@
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
 
@@ -12,42 +13,46 @@ class AppSimulator:
12
  self.simulation = None
13
  self.openai_api_key = openai_api_key
14
 
15
- def initialize_simulator(self, task, app_name, sitemap, progress=gr.Progress(track_tqdm=True)):
16
  """Initialize the simulator with retries and elapsed time tracking."""
17
- retry_count = 0
18
- max_retries = 50
19
-
20
- while retry_count < max_retries:
21
- try:
22
- # Display current attempt with progress bar
23
- progress.update(f"Initializing... Attempt {retry_count + 1}/{max_retries}")
24
-
25
- # Process data and initialize the simulation
26
- data_population = DataPopulation(api_key=self.openai_api_key)
27
- sitemap_data, page_details, user_state = data_population.process_data(task, sitemap)
28
-
29
- self.simulation = ChatbotSimulation(
30
- site_map=sitemap_data,
31
- page_details=page_details,
32
- user_state=user_state,
33
- task=task,
34
- app_name=app_name,
35
- log_location=f'conversation_log_{app_name}_human.txt',
36
- openai_api_key=self.openai_api_key,
37
- agent='human'
38
- )
39
-
40
- # Successful initialization
41
- initial_message = self.simulation.start_conversation()
42
- progress.update("Initialization Successful")
43
- return initial_message # Return the initial assistant message for chat
44
- except Exception as e:
45
- retry_count += 1
46
- print(f"Attempt {retry_count}/{max_retries} failed: {e}")
47
-
48
- # If all retries failed
49
- progress.update("Failed to initialize simulator after multiple retries.")
50
- return "Initialization failed." # Error message for chat
 
 
 
 
51
 
52
  def chat_interaction(self, user_input, history):
53
  """Handle one round of conversation."""
@@ -57,19 +62,11 @@ class AppSimulator:
57
  # Initialize the simulator
58
  simulator_app = AppSimulator(openai_api_key=openai_api_key)
59
 
60
- def is_valid_input(user_input):
61
- """Validate user input format."""
62
- pattern = r"^\d+\.\s+.*"
63
- return bool(re.match(pattern, user_input))
64
 
65
  def chat(user_input, history):
66
  """Chat handler that validates input and interacts with the simulator."""
67
- if is_valid_input(user_input):
68
- valid_response = simulator_app.chat_interaction(user_input, history)
69
- return valid_response
70
- else:
71
- invalid_response = "Invalid input. Please use the format: Number. Description: query"
72
- return invalid_response
73
 
74
 
75
  # Gradio Interface using ChatInterface
@@ -77,8 +74,6 @@ with gr.Blocks(fill_height=True) as demo:
77
  gr.Markdown("## Simulator Setup")
78
 
79
  # Input fields for initialization
80
- task_input = gr.Textbox(label="Task", placeholder="Describe your task...")
81
- app_name_input = gr.Textbox(label="App Name", placeholder="Enter the app name... (eg. DoorDash)")
82
  sitemap_input = gr.Textbox(label="Sitemap", placeholder="Enter the Hugging Face link to sitemap... (eg.jjz5463/DoorDash_synthetic_sitemap)")
83
  initialize_button = gr.Button("Initialize Simulator")
84
 
@@ -89,13 +84,13 @@ with gr.Blocks(fill_height=True) as demo:
89
  chat_interface = gr.ChatInterface(fn=chat, type='messages')
90
 
91
  # Define the callback function to initialize the simulator and update status
92
- def initialize_and_start_chat(task, app_name, sitemap):
93
- return simulator_app.initialize_simulator(task, app_name, sitemap) # Use progress tracking
94
 
95
  # Set up the button click to initialize simulator and update status only
96
  initialize_button.click(
97
  fn=initialize_and_start_chat,
98
- inputs=[task_input, app_name_input, sitemap_input],
99
  outputs=status # Update only the status block
100
  )
101
 
 
1
  import gradio as gr
2
  from chatbot_simulator import ChatbotSimulation
3
  from task_specific_data_population import DataPopulation
4
+ from datasets import load_dataset
5
+ import json_repair
6
+ import random
7
 
8
  openai_api_key = os.getenv("OPENAI_API_KEY")
9
 
 
13
  self.simulation = None
14
  self.openai_api_key = openai_api_key
15
 
16
+ def initialize_simulator(self, sitemap_url, progress=gr.Progress(track_tqdm=True)):
17
  """Initialize the simulator with retries and elapsed time tracking."""
18
+ synthetic_sitemap = load_dataset(sitemap_url, "sitemap", split='train')
19
+ app_name, sitemap, page_details, user_state, system_data = None, None, None, None, None
20
+ for row in synthetic_sitemap:
21
+ if row['name'] == 'app_name':
22
+ app_name = row['value'] # Use `eval` to convert the string to a list
23
+ elif row['name'] == 'sitemap':
24
+ sitemap = json_repair.loads(row['value'])
25
+ elif row['name'] == 'page_details':
26
+ page_details = json_repair.loads(row['value'])
27
+ elif row['name'] == 'user_state':
28
+ user_state = json_repair.loads(row['value'])
29
+ elif row['name'] == 'system_data':
30
+ system_data = json_repair.loads(row['value'])
31
+
32
+ synthetic_tasks = load_dataset(sitemap_url, "tasks", split='train')
33
+ random_index = random.randint(0, len(synthetic_tasks) - 1) # Generate a random index
34
+ random_row = synthetic_tasks[random_index]
35
+ task = random_row['tasks']
36
+ solution = random_row['steps']
37
+ user_data = random_row['attributes']['user_data']
38
+
39
+ self.simulation = ChatbotSimulation(
40
+ app_name=app_name,
41
+ site_map=sitemap,
42
+ page_details=page_details,
43
+ user_state=user_state,
44
+ system_data=system_data,
45
+ user_data=user_data,
46
+ task=task,
47
+ solution=solution,
48
+ log_location=f'conversation_log_{app_name}.txt',
49
+ openai_api_key=openai_api_key,
50
+ agent='llm'
51
+ )
52
+
53
+ initial_message = self.simulation.start_conversation()
54
+ progress.update("Initialization Successful")
55
+ return initial_message # Return the initial assistant message for chat
56
 
57
  def chat_interaction(self, user_input, history):
58
  """Handle one round of conversation."""
 
62
  # Initialize the simulator
63
  simulator_app = AppSimulator(openai_api_key=openai_api_key)
64
 
 
 
 
 
65
 
66
  def chat(user_input, history):
67
  """Chat handler that validates input and interacts with the simulator."""
68
+ response = simulator_app.chat_interaction(user_input, history)
69
+ return response
 
 
 
 
70
 
71
 
72
  # Gradio Interface using ChatInterface
 
74
  gr.Markdown("## Simulator Setup")
75
 
76
  # Input fields for initialization
 
 
77
  sitemap_input = gr.Textbox(label="Sitemap", placeholder="Enter the Hugging Face link to sitemap... (eg.jjz5463/DoorDash_synthetic_sitemap)")
78
  initialize_button = gr.Button("Initialize Simulator")
79
 
 
84
  chat_interface = gr.ChatInterface(fn=chat, type='messages')
85
 
86
  # Define the callback function to initialize the simulator and update status
87
+ def initialize_and_start_chat(sitemap):
88
+ return simulator_app.initialize_simulator(sitemap) # Use progress tracking
89
 
90
  # Set up the button click to initialize simulator and update status only
91
  initialize_button.click(
92
  fn=initialize_and_start_chat,
93
+ inputs=[sitemap_input],
94
  outputs=status # Update only the status block
95
  )
96
 
chatbot_simulator.py CHANGED
@@ -3,20 +3,26 @@ import json_repair
3
  from transformers import AutoTokenizer
4
  from openai import RateLimitError
5
  import time
 
 
6
 
7
 
8
  class ChatbotSimulation:
9
- def __init__(self, site_map, page_details, user_state, task,
10
- app_name, log_location, openai_api_key, agent='human',
11
  max_steps=50, max_tokens=8192, buffer_tokens=500):
 
12
  self.sitemap = site_map
13
  self.page_details = page_details
14
  self.user_state = user_state
15
  self.user_state['current_page'] = 'Home' # Initialize current page
16
- self.user_state['last_page'] = 'Home'
17
  self.user_state['task_completed'] = 'False'
 
 
 
18
  self.task = task
19
- self.app_name = app_name
 
20
  self.log_location = log_location
21
  self.agent = agent.lower()
22
  if self.agent not in ['human', 'llm']:
@@ -28,11 +34,14 @@ class ChatbotSimulation:
28
  self.prompt_count = 0
29
  self.client = OpenAI(api_key=openai_api_key)
30
  self.actions = []
31
- self.tokenizer = AutoTokenizer.from_pretrained("gpt2")
 
 
 
32
 
33
  def _get_page_uid(self, page_name):
34
  """Retrieve the UID of the given page from the sitemap."""
35
- return self.sitemap['pages'].get(page_name, {}).get('uid')
36
 
37
  def _get_page_details(self, page_name):
38
  """Retrieve the page details using its UID."""
@@ -41,43 +50,22 @@ class ChatbotSimulation:
41
 
42
  def _generate_system_prompt(self):
43
  """Create a dynamic system prompt based on the current state."""
44
- current_page = self.user_state['current_page']
45
- last_page = self.user_state['last_page']
 
 
46
  page_info = self._get_page_details(current_page)
47
 
48
- return f"""
49
- You are a text-based simulator of {self.app_name} app.
50
- You are interacting with a user. User's task is: {self.task}.
51
- User's last page was {last_page} and the user have taken actions: {self.actions}.
52
- After action, user is currently on the {current_page} page.
53
- Current user state: {self.user_state}.
54
-
55
- Page Information:
56
- - **If the user requests page you do not possess** (such as a list of restaurants, menus, or similar details),
57
- you are permitted to create plausible and relevant information to fulfill the request.
58
- Present this fabricated information convincingly as if it were real data.
59
- {page_info}
60
-
61
- - **Features**: Represent available options the user can select on this page.
62
- - **User Data**: Represents user-specific data accessible on this page.
63
-
64
- Provide instructions or request input from the user. If the user provides an invalid action, respond with:
65
- "Invalid action. Please select a valid option."
66
-
67
- ### Instruction Format:
68
- <if actions is non-empty: You have successfully done actions[-1]> You are at the {current_page} page. You have the following options:
69
- 1. Feature 1
70
- 2. Feature 2
71
- 3. Feature 3
72
- 4. Feature 4
73
-
74
- Please enter your choice as 'Number. Description'. If you have a query, enter as 'Number. Description: query'
75
-
76
- Rules:
77
- - Be sure to display all options that is available in features.
78
- - Be robotic and emotionless. Avoid offering any advice to the user.
79
- - Make sure there is always a back to last_page and Home page button.
80
- """
81
 
82
  def _get_openai_response(self, prompt):
83
  """Fetch response from OpenAI API."""
@@ -120,81 +108,139 @@ Rules:
120
  def one_conversation_round(self, user_input):
121
  """Conduct one round of conversation between the user and the assistant."""
122
  # User provides input
 
 
 
 
 
 
123
  self.actions.append(user_input + f'on {self.user_state["current_page"]} page')
124
  self.conversation.append({"role": "user", "content": user_input})
125
  self.prompt_count += 1
126
 
127
  # Update user state using GPT's response
128
- update_prompt = f"""
129
- If user takes action '{user_input}' on {self.user_state['current_page']} page, which page will they move to?
130
- Recall user's task: {self.task}
131
- Update the user_state dictionary based on user's last action:
132
- Current user_state: {self.user_state}
133
- Sitemap: {self.sitemap}
134
-
135
- Instructions:
136
- 1. If the 'current_page' has changed, update it to a page from the sitemap.
137
- 2. If the task is finished, update 'task_completed' to True. Otherwise, leave it as False.
138
- 3. If no updates are needed, return the user state exactly as provided, without modification.
139
-
140
- Important:
141
- - Ensure 'current_page' and 'task_completed' are keys in the returned dictionary.
142
- - Return only the dictionary without additional output or wrapping.
143
-
144
- Example Output Format:
145
- {{
146
- 'current_page': 'Home',
147
- 'last_page': 'Home',
148
- 'task_completed': 'False',
149
- }}
150
- """
151
 
152
  self.conversation.append({"role": "assistant", "content": update_prompt})
153
- updated_state = self._get_openai_response(self.conversation)
154
  self.conversation.pop(-1) ## update prompt don't have to stay in conversation history
155
 
156
  # Parse and update the user state
157
  updated_state = json_repair.loads(updated_state)
158
 
159
- required_keys = {'current_page', 'last_page', 'task_completed'}
160
- while not isinstance(updated_state, dict) or not required_keys.issubset(updated_state.keys()):
161
- transform_prompt = f""""
162
- Transform {updated_state} to a properly formate JSON file.
 
 
163
  Example Output Format:
164
  {{
165
- 'current_page': 'Home',
166
- 'last_page': 'Home',
167
- 'task_completed': 'False',
168
  }}
169
  """
170
  updated_state = self._get_openai_response([{"role": "system", "content": transform_prompt}])
171
  updated_state = json_repair.loads(updated_state)
 
 
 
 
 
 
 
172
 
173
  try:
174
- if updated_state['task_completed'].lower() == 'true':
175
  return f"Task completed! You took {self.prompt_count} steps."
176
  except:
177
  updated_state['task_completed'] = 'False'
178
 
179
  self.user_state = updated_state
 
 
 
 
180
 
181
- #self.conversation.clear()
182
- system_prompt = self._generate_system_prompt()
183
-
184
- # GPT generates the page instructions
185
  ## no need to store old system prompt while we get a new one
186
  self.conversation = [entry for entry in self.conversation if entry["role"] != "system"]
 
 
187
  self.conversation.append({"role": "system", "content": system_prompt})
188
-
189
  gpt_instruction = self._get_openai_response(self.conversation)
190
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
191
  return gpt_instruction
192
 
193
  def start_conversation(self):
194
- greeting = f'\n Welcome to {self.app_name} simulator! Your task is: {self.task}. \n'
195
  system_prompt = self._generate_system_prompt()
196
  # GPT generates the page instructions
197
  self.conversation.append({"role": "system", "content": system_prompt})
198
  gpt_instruction = self._get_openai_response(self.conversation)
199
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
200
  return greeting + gpt_instruction
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from transformers import AutoTokenizer
4
  from openai import RateLimitError
5
  import time
6
+ from prompts import *
7
+ import re
8
 
9
 
10
  class ChatbotSimulation:
11
+ def __init__(self, app_name, site_map, page_details, user_state, system_data, user_data, task, solution,
12
+ log_location, openai_api_key, agent='human',
13
  max_steps=50, max_tokens=8192, buffer_tokens=500):
14
+ self.app_name = app_name
15
  self.sitemap = site_map
16
  self.page_details = page_details
17
  self.user_state = user_state
18
  self.user_state['current_page'] = 'Home' # Initialize current page
 
19
  self.user_state['task_completed'] = 'False'
20
+ self.user_state['back'] = 'False'
21
+ self.system_data = system_data
22
+ self.user_data = user_data
23
  self.task = task
24
+ self.solution = solution
25
+
26
  self.log_location = log_location
27
  self.agent = agent.lower()
28
  if self.agent not in ['human', 'llm']:
 
34
  self.prompt_count = 0
35
  self.client = OpenAI(api_key=openai_api_key)
36
  self.actions = []
37
+ self.tokenizer = AutoTokenizer.from_pretrained("gpt2", clean_up_tokenization_spaces=True)
38
+
39
+ # back button
40
+ self.page_history = ['Home']
41
 
42
  def _get_page_uid(self, page_name):
43
  """Retrieve the UID of the given page from the sitemap."""
44
+ return self.sitemap.get(page_name, {}).get('uid')
45
 
46
  def _get_page_details(self, page_name):
47
  """Retrieve the page details using its UID."""
 
50
 
51
  def _generate_system_prompt(self):
52
  """Create a dynamic system prompt based on the current state."""
53
+ #current_page = self.user_state['current_page']
54
+ #last_page = self.user_state['last_page']
55
+ current_page = self.page_history[-1] if len(self.page_history) >= 1 else "Home"
56
+ last_page = self.page_history[-2] if len(self.page_history) > 1 else "Home"
57
  page_info = self._get_page_details(current_page)
58
 
59
+ return get_system_prompt(app_name=self.app_name,
60
+ system_data=self.system_data,
61
+ task=self.task,
62
+ user_data=self.user_data,
63
+ current_page=current_page,
64
+ last_page=last_page,
65
+ actions=self.actions,
66
+ user_state=self.user_state,
67
+ page_info=page_info
68
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  def _get_openai_response(self, prompt):
71
  """Fetch response from OpenAI API."""
 
108
  def one_conversation_round(self, user_input):
109
  """Conduct one round of conversation between the user and the assistant."""
110
  # User provides input
111
+ valid_input = self._is_valid_input(user_input)
112
+ if valid_input[0]:
113
+ pass
114
+ else:
115
+ return f"\n{self.app_name}: Invalid input. {valid_input[1]}"
116
+
117
  self.actions.append(user_input + f'on {self.user_state["current_page"]} page')
118
  self.conversation.append({"role": "user", "content": user_input})
119
  self.prompt_count += 1
120
 
121
  # Update user state using GPT's response
122
+ current_page = self.page_history[-1] if len(self.page_history) >= 1 else "Home"
123
+ update_prompt = get_user_state_update_prompt(user_input=user_input,
124
+ current_page=current_page,
125
+ task=self.task,
126
+ solution=self.solution,
127
+ user_state=self.user_state,
128
+ sitemap=self.sitemap)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  self.conversation.append({"role": "assistant", "content": update_prompt})
131
+ updated_state = self._get_openai_response(self.conversation).split("UPDATED", 1)[1].strip()
132
  self.conversation.pop(-1) ## update prompt don't have to stay in conversation history
133
 
134
  # Parse and update the user state
135
  updated_state = json_repair.loads(updated_state)
136
 
137
+ # format forcing of updated state
138
+ required_keys = {'current_page', 'task_completed', 'back'}
139
+ # Ensure `updated_state` is a dictionary
140
+ while not isinstance(updated_state, dict):
141
+ transform_prompt = f"""
142
+ Transform {updated_state} to a properly formatted JSON file.
143
  Example Output Format:
144
  {{
145
+ 'current_page': 'Home',
146
+ 'task_completed': False,
147
+ 'back': False
148
  }}
149
  """
150
  updated_state = self._get_openai_response([{"role": "system", "content": transform_prompt}])
151
  updated_state = json_repair.loads(updated_state)
152
+ # Manually add missing required keys
153
+ for key in required_keys:
154
+ if key not in updated_state:
155
+ if key == 'current_page':
156
+ updated_state[key] = self.page_history[-1] if len(self.page_history) >= 1 else "Home"
157
+ else:
158
+ updated_state[key] = False
159
 
160
  try:
161
+ if str(updated_state['task_completed']).lower() == 'true':
162
  return f"Task completed! You took {self.prompt_count} steps."
163
  except:
164
  updated_state['task_completed'] = 'False'
165
 
166
  self.user_state = updated_state
167
+ if str(updated_state['back']).lower() == 'false':
168
+ self.page_history.append(updated_state['current_page'])
169
+ elif self.page_history:
170
+ self.page_history.pop()
171
 
 
 
 
 
172
  ## no need to store old system prompt while we get a new one
173
  self.conversation = [entry for entry in self.conversation if entry["role"] != "system"]
174
+ system_prompt = self._generate_system_prompt()
175
+ # GPT generates the page instructions
176
  self.conversation.append({"role": "system", "content": system_prompt})
 
177
  gpt_instruction = self._get_openai_response(self.conversation)
178
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
179
  return gpt_instruction
180
 
181
  def start_conversation(self):
182
+ greeting = f'\nWelcome to {self.app_name} simulator! Your task is: {self.task}. \n'
183
  system_prompt = self._generate_system_prompt()
184
  # GPT generates the page instructions
185
  self.conversation.append({"role": "system", "content": system_prompt})
186
  gpt_instruction = self._get_openai_response(self.conversation)
187
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
188
  return greeting + gpt_instruction
189
+
190
+ def _extract_buttons(self):
191
+ """Extract buttons and their action types from the latest conversation if role is 'assistant'."""
192
+ # Get the last message
193
+ last_message = self.conversation[-1]
194
+
195
+ # Ensure the role of the last message is 'assistant'
196
+ if last_message.get("role") != "assistant":
197
+ return {}
198
+
199
+ # Extract the content of the last message
200
+ message_content = last_message.get("content", "")
201
+
202
+ # Make the split case-insensitive by searching for the phrase with re.IGNORECASE
203
+ options_split = re.split(r"you have the following options:", message_content, flags=re.IGNORECASE)
204
+
205
+ # If the split doesn't produce at least two parts, return an empty dictionary
206
+ if len(options_split) < 2:
207
+ return {}
208
+
209
+ # Extract button definitions from the second part of the split content
210
+ button_section = options_split[1]
211
+ pattern = r"\d+\.\s+(.*?):\s+([a-zA-Z_]+)"
212
+ buttons = re.findall(pattern, button_section)
213
+
214
+ # Construct the dictionary with button names as keys and action types as values
215
+ return {name.strip().lower(): action_type.strip().lower() for name, action_type in buttons}
216
+
217
+ def _is_valid_input(self, user_input):
218
+ """Validate user input format."""
219
+ valid_buttons = self._extract_buttons()
220
+
221
+ # Validate input format
222
+ pattern = r"^(?P<action_type>\w+)\((?P<button_name>[^,]+)(?:,\s*(?P<query>.+))?\)$"
223
+ match = re.match(pattern, user_input)
224
+
225
+ if not match:
226
+ return [False, "Your input doesn't match the format: action_type(button name), OR if type, use type(button name, query)"]
227
+
228
+ # Extract parsed components
229
+ action_type = match.group("action_type").lower()
230
+ button_name = match.group("button_name").strip().lower()
231
+ query = match.group("query") # Optional query for `type`
232
+
233
+ # Validate button name and action type
234
+ if button_name not in valid_buttons:
235
+ return [False,
236
+ "Invalid Button name! Recall: Each button is in the format: `number. button name: action_type`"] # Button name must match exactly (case insensitive)
237
+ if action_type != valid_buttons[button_name]:
238
+ return [False,
239
+ "Invalid action type! Recall: Each button is in the format: `number. button name: action_type`"] # Action type must match the button's specified type
240
+ if action_type == "type" and query is None:
241
+ return [False,
242
+ "Missing Query for action type 'type'! Recall: use the format: `type(button name, query)`"] # `type` action requires a query
243
+ if action_type != "type" and query is not None:
244
+ return [False,
245
+ "Non-`type` action_type cannot take query!"] # Non-`type` actions must not have a query
246
+ return [True, 'Pass']
prompts.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ def get_system_prompt(app_name, system_data, task, user_data, current_page, last_page, actions, user_state, page_info):
3
+ system_prompt = f"""
4
+ You are a text-based {app_name}-like app simulator game.
5
+ The {app_name} app contains the following system data: {system_data}.
6
+ You are interacting with a user whose task is: {task}.
7
+ User's data: {user_data}.
8
+ The user's last page was {last_page}, and they have taken the following actions: {actions}.
9
+ After performing the most recent action, the user is now on the {current_page} page.
10
+ Current user state: {user_state}.
11
+
12
+ Details and buttons on the current page:
13
+ {page_info}
14
+
15
+ Each button on the page has a `name` and an `action_type`.
16
+
17
+ ### Instruction Format:
18
+ <If `actions` is non-empty: "You have successfully performed actions[-1]">
19
+ <If current_page == log in & user_state['logged_in'] == False: You need to log in first. Your credentials are: Username: user_data['credentials']['user_id'] Password: user_data['credentials']['password']>
20
+ <If current_page == log in & user_state['logged_in'] == True: You have successfully logged in as user_data['credentials']['user_id']>
21
+ You are currently on the **{current_page}** page. You have the following options:
22
+ 1. option 1: action type
23
+ 2. option 2: action type
24
+ 3. option 3: action type
25
+ 4. option 4: action type
26
+ 5. Back to last page: click
27
+ 6. Back to Home page: click
28
+ 7. <If user_state['logged_in'] == False, display Log In: click to take user to log in page>
29
+ 8. Do nothing: noop
30
+
31
+ Each button is in the format: `number. button name: action_type`.
32
+ Please provide your choice in the format: `action_type(button name)`.
33
+ If you have a query, use the format: `type(button name, query)`.
34
+ Please provide only one choice per turn.
35
+
36
+ ### **Rules**:
37
+ 1. Display all available options from the buttons on the page.
38
+ 2. Remain robotic and emotionless. Avoid offering any advice or opinions to the user.
39
+ 3. Ensure there is always a button to navigate back to the **{last_page}** and a button to go to the **Home page**.
40
+ 4. Ensure there is always a button that allow user to do nothing.
41
+ 5. Ensure the action type is a single word, such as ‘type’ or ‘click.’ If the action type is not a single word, convert it to a single word. For example, type(SUMMER, apply) should be transformed to type.
42
+ 6. Group similar action types under ‘type’ and display them as ‘type’ (e.g., display ‘input’ or ‘search’ as ‘type’).
43
+ """
44
+ return system_prompt
45
+
46
+
47
+ def get_user_state_update_prompt(user_input, current_page, task, solution, user_state, sitemap):
48
+ structure = """
49
+ {
50
+ 'current_page': 'Home',
51
+ 'task_completed': False,
52
+ 'back': False
53
+ }
54
+ """
55
+ update_prompt = f"""
56
+ You will now update the user state.
57
+ The user takes the action '{user_input}' on the {current_page} page. Determine which page the user will move to next.
58
+ The next page must from the sitemap: {sitemap}
59
+ Recall the user's task: {task}. Solution to the user's task: {solution}.
60
+ Update the `user_state` dictionary based on the user's last action:
61
+ Current user_state: {user_state}.
62
+
63
+ ### Instructions:
64
+ 1. If the 'current_page' has changed, update it to a valid page from the sitemap.
65
+ 2. If the task is completed, update 'task_completed' to `True`. Otherwise, leave it as `False`.
66
+ 3. If no updates are needed, return the `user_state` exactly as provided, without any changes.
67
+
68
+ ### Important Notes:
69
+ - Ensure 'current_page' and 'task_completed' are always present as keys in the returned dictionary.
70
+ - If the user go back a page from current page, set 'back' to `True`. Otherwise, it should remain `False`.
71
+ - Return only the updated dictionary, without additional text, explanations, or wrapping.
72
+
73
+ ### Output Format:
74
+ 1. Start with "REASON": Explain which fields in `user_state` need to be updated and why.
75
+ 2. End with "UPDATED": Provide only the updated `user_state` dictionary. Follow the input user_state schema structure exactly without modifying its organization. Do not include explanations or any additional text after updated user state.
76
+ """
77
+ return update_prompt
78
+
79
+
80
+ def get_agent_prompt(app_name, task, conversation):
81
+ agent_prompt = f"""
82
+ Imagine you are an agent navigate through the {app_name} environment.
83
+ Your overarching task is: {task}. You may have done some part of the task, or none at all.
84
+ You will have access to all of your previous actions in the environment, as well as the last message from the assistant giving the current state of the environment.
85
+ The last message from the assistant was: {conversation[-1]['content']}
86
+ Respond first with a brief "Plan" which suggests what steps you are going to take to accomplish the task, and what your immediate.
87
+ Then generate an "Action" which is the immediate next step you can take.
88
+ """
89
+ return agent_prompt