jjz5463 commited on
Commit
700f11b
1 Parent(s): 86fad57

random pick an incomplete task instead of go with sequential order

Browse files
Files changed (3) hide show
  1. app.py +28 -22
  2. chatbot_simulator.py +57 -32
  3. prompts.py +66 -83
app.py CHANGED
@@ -38,12 +38,13 @@ def find_random_incomplete_task(app_name):
38
  return random_idx
39
 
40
 
41
- def write_task_data(app_name, idx, task, task_complete, task_completed_step):
42
  doc_ref = db.collection(app_name).document(f"{app_name}_{idx}")
43
  doc_ref.set({
44
  "task": task,
45
  "task_completed": task_complete,
46
- "task_completed_steps": task_completed_step
 
47
  })
48
 
49
 
@@ -57,19 +58,20 @@ class AppSimulator:
57
 
58
  def initialize_simulator(self, sitemap_url, progress=gr.Progress(track_tqdm=True)):
59
  """Initialize the simulator with retries and elapsed time tracking."""
60
- synthetic_sitemap = load_dataset(sitemap_url, "sitemap", split='train')
61
- app_name, sitemap, page_details, user_state, system_data = None, None, None, None, None
 
62
  for row in synthetic_sitemap:
63
  if row['name'] == 'app_name':
64
- app_name = row['value'] # Use `eval` to convert the string to a list
 
 
65
  elif row['name'] == 'sitemap':
66
  sitemap = json_repair.loads(row['value'])
67
- elif row['name'] == 'page_details':
68
- page_details = json_repair.loads(row['value'])
69
- elif row['name'] == 'user_state':
70
- user_state = json_repair.loads(row['value'])
71
- elif row['name'] == 'system_data':
72
- system_data = json_repair.loads(row['value'])
73
 
74
  self.app_name = app_name
75
  smallest_index = find_random_incomplete_task(app_name)
@@ -78,25 +80,26 @@ class AppSimulator:
78
  self.smallest_index = smallest_index
79
 
80
  synthetic_tasks = load_dataset(sitemap_url, "tasks", split='train')
81
- incomplete_task = synthetic_tasks[smallest_index]
82
- task = incomplete_task["tasks"]
83
- solution = incomplete_task["steps"]
84
- user_data = incomplete_task["attributes"]["user_data"]
 
85
 
86
  self.task = task
87
 
88
  self.simulation = ChatbotSimulation(
89
  app_name=app_name,
 
90
  site_map=sitemap,
91
- page_details=page_details,
92
- user_state=user_state,
93
- system_data=system_data,
94
- user_data=user_data,
95
  task=task,
96
  solution=solution,
97
  log_location=f'conversation_log_{app_name}.txt',
98
  openai_api_key=openai_api_key,
99
- agent='llm'
100
  )
101
 
102
  initial_message = self.simulation.start_conversation()
@@ -124,7 +127,9 @@ def chat(user_input, history, simulator_app):
124
  app_name = simulator_app.app_name
125
  idx = simulator_app.smallest_index
126
  task = simulator_app.task
127
- write_task_data(app_name, idx, task, task_complete, task_completed_step)
 
 
128
 
129
  return response
130
 
@@ -137,8 +142,9 @@ def give_up(simulator_app):
137
  app_name = simulator_app.app_name
138
  idx = simulator_app.smallest_index
139
  task = simulator_app.task
 
140
 
141
- write_task_data(app_name, idx, task, task_completed, task_completed_steps)
142
 
143
  return "Task marked as abandoned (Give-Up action)."
144
 
 
38
  return random_idx
39
 
40
 
41
+ def write_task_data(app_name, idx, task, task_complete, task_completed_step, trajectory):
42
  doc_ref = db.collection(app_name).document(f"{app_name}_{idx}")
43
  doc_ref.set({
44
  "task": task,
45
  "task_completed": task_complete,
46
+ "task_completed_steps": task_completed_step,
47
+ "trajectory": trajectory
48
  })
49
 
50
 
 
58
 
59
  def initialize_simulator(self, sitemap_url, progress=gr.Progress(track_tqdm=True)):
60
  """Initialize the simulator with retries and elapsed time tracking."""
61
+ synthetic_sitemap = load_dataset(sitemap_url, "schema", split='train')
62
+ app_name, app_description, sitemap, relevant_tables_per_page, jinjia_prerender_page = None, None, None, None, None
63
+
64
  for row in synthetic_sitemap:
65
  if row['name'] == 'app_name':
66
+ app_name = row['value']
67
+ elif row['name'] == 'app_description':
68
+ app_description = row['value']
69
  elif row['name'] == 'sitemap':
70
  sitemap = json_repair.loads(row['value'])
71
+ elif row['name'] == 'relevant_tables_per_page':
72
+ relevant_tables_per_page = json_repair.loads(row['value'])
73
+ elif row['name'] == 'jinjia_prerender_pages':
74
+ jinjia_prerender_page = json_repair.loads(row['value'])
 
 
75
 
76
  self.app_name = app_name
77
  smallest_index = find_random_incomplete_task(app_name)
 
80
  self.smallest_index = smallest_index
81
 
82
  synthetic_tasks = load_dataset(sitemap_url, "tasks", split='train')
83
+ random_index = random.randint(0, len(synthetic_tasks) - 1) # Generate a random index
84
+ random_row = synthetic_tasks[random_index]
85
+ task = random_row['task']
86
+ solution = random_row['solution']
87
+ database = random_row['database']
88
 
89
  self.task = task
90
 
91
  self.simulation = ChatbotSimulation(
92
  app_name=app_name,
93
+ app_description=app_description,
94
  site_map=sitemap,
95
+ relevant_tables_per_page=relevant_tables_per_page,
96
+ database=database,
97
+ jinjia_prerender_page=jinjia_prerender_page,
 
98
  task=task,
99
  solution=solution,
100
  log_location=f'conversation_log_{app_name}.txt',
101
  openai_api_key=openai_api_key,
102
+ agent='Human'
103
  )
104
 
105
  initial_message = self.simulation.start_conversation()
 
127
  app_name = simulator_app.app_name
128
  idx = simulator_app.smallest_index
129
  task = simulator_app.task
130
+ trajectory = simulator_app.simulation.trajectory
131
+
132
+ write_task_data(app_name, idx, task, task_complete, task_completed_step, trajectory)
133
 
134
  return response
135
 
 
142
  app_name = simulator_app.app_name
143
  idx = simulator_app.smallest_index
144
  task = simulator_app.task
145
+ trajectory = simulator_app.simulation.trajectory
146
 
147
+ write_task_data(app_name, idx, task, task_completed, task_completed_steps, trajectory)
148
 
149
  return "Task marked as abandoned (Give-Up action)."
150
 
chatbot_simulator.py CHANGED
@@ -5,24 +5,28 @@ from prompts import *
5
  import re
6
  from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type
7
  from openai import RateLimitError
 
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']:
@@ -31,40 +35,53 @@ class ChatbotSimulation:
31
  self.max_tokens = max_tokens
32
  self.buffer_tokens = buffer_tokens
33
  self.conversation = [] # Stores recent conversation snippets
 
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."""
48
- uid = self._get_page_uid(page_name)
49
- return self.page_details.get(uid, {})
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  def _generate_system_prompt(self):
52
  """Create a dynamic system prompt based on the current state."""
53
- current_page = self.page_history[-1] if len(self.page_history) >= 1 else "Home"
54
- last_page = self.page_history[-2] if len(self.page_history) > 1 else "Home"
55
- page_info = self._get_page_details(current_page)
56
- page_linkage = self.sitemap.get(current_page, {}).get('links_to')
 
57
 
58
  return get_system_prompt(app_name=self.app_name,
59
- system_data=self.system_data,
 
60
  task=self.task,
61
- user_data=self.user_data,
62
  current_page=current_page,
63
  last_page=last_page,
64
  actions=self.actions,
65
- user_state=self.user_state,
66
- page_info=page_info,
67
- page_linkage=page_linkage
68
  )
69
 
70
  @retry(
@@ -100,28 +117,33 @@ class ChatbotSimulation:
100
  def one_conversation_round(self, user_input):
101
  """Conduct one round of conversation between the user and the assistant."""
102
  # User provides input
 
103
  valid_input = self._is_valid_input(user_input)
104
  if valid_input[0]:
105
  pass
106
  else:
107
- return f"\n{self.app_name}: Invalid input. {valid_input[1]}"
 
 
 
108
 
109
  self.actions.append(user_input + f'on {self.user_state["current_page"]} page')
110
  self.conversation.append({"role": "user", "content": user_input})
111
  self.prompt_count += 1
112
 
113
  # Update user state using GPT's response
114
- current_page = self.page_history[-1] if len(self.page_history) >= 1 else "Home"
115
  update_prompt = get_user_state_update_prompt(user_input=user_input,
116
  current_page=current_page,
117
  task=self.task,
 
118
  solution=self.solution,
119
  user_state=self.user_state,
120
  sitemap=self.sitemap)
121
 
122
- self.conversation.append({"role": "assistant", "content": update_prompt})
123
  updated_state = self._get_openai_response(self.conversation).split("UPDATED", 1)[1].strip()
124
- self.conversation.pop(-1) ## update prompt don't have to stay in conversation history
125
 
126
  # Parse and update the user state
127
  updated_state = json_repair.loads(updated_state)
@@ -151,7 +173,9 @@ class ChatbotSimulation:
151
 
152
  try:
153
  if str(updated_state['task_completed']).lower() == 'true':
154
- return f"Task completed! You took {self.prompt_count} steps."
 
 
155
  except:
156
  updated_state['task_completed'] = 'False'
157
 
@@ -168,6 +192,7 @@ class ChatbotSimulation:
168
  self.conversation.append({"role": "system", "content": system_prompt})
169
  gpt_instruction = self._get_openai_response(self.conversation)
170
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
 
171
  return gpt_instruction
172
 
173
  def start_conversation(self):
 
5
  import re
6
  from tenacity import retry, wait_fixed, stop_after_attempt, retry_if_exception_type
7
  from openai import RateLimitError
8
+ from difflib import get_close_matches
9
 
10
 
11
  class ChatbotSimulation:
12
+ def __init__(self, app_name, app_description, site_map, relevant_tables_per_page,
13
+ database, jinjia_prerender_page, task, solution,
14
  log_location, openai_api_key, agent='human',
15
+ max_steps=30, max_tokens=8192, buffer_tokens=500):
16
  self.app_name = app_name
17
+ self.app_description = app_description
18
  self.sitemap = site_map
19
+ self.relevant_tables_per_page = relevant_tables_per_page
20
+ self.database = database
21
+ self.jinjia_prerender_page = jinjia_prerender_page
 
 
 
 
22
  self.task = task
23
  self.solution = solution
24
 
25
+ self.user_state = dict()
26
+ self.user_state['current_page'] = self.sitemap['pages'][0]['id'] # Initialize current page
27
+ self.user_state['task_completed'] = 'False'
28
+ self.user_state['back'] = 'False'
29
+
30
  self.log_location = log_location
31
  self.agent = agent.lower()
32
  if self.agent not in ['human', 'llm']:
 
35
  self.max_tokens = max_tokens
36
  self.buffer_tokens = buffer_tokens
37
  self.conversation = [] # Stores recent conversation snippets
38
+ self.trajectory = [{"role": "system", "content": f"Welcome to {app_name} simulator! Your task is: {task}"}]
39
  self.prompt_count = 0
40
  self.client = OpenAI(api_key=openai_api_key)
41
  self.actions = []
42
  self.tokenizer = AutoTokenizer.from_pretrained("gpt2", clean_up_tokenization_spaces=True)
43
 
44
+ #back button
45
  self.page_history = ['Home']
46
 
47
+ def _get_relevant_data(self, current_page):
48
+ # Check if the current page exists as a key
49
+ if current_page in self.relevant_tables_per_page:
50
+ relevant_tables = self.relevant_tables_per_page[current_page]
51
+ else:
52
+ # Find the closest matching key
53
+ closest_match = get_close_matches(current_page, self.relevant_tables_per_page.keys(), n=1, cutoff=0.5)
54
+ if closest_match:
55
+ relevant_tables = self.relevant_tables_per_page[closest_match[0]]
56
+ else:
57
+ return self.database
58
+
59
+ return {table: self.database[table] for table in relevant_tables if table in self.database}
60
+
61
+ def _get_prerender_page(self, current_page):
62
+ if current_page in self.jinjia_prerender_page:
63
+ return self.jinjia_prerender_page[current_page]
64
+ else:
65
+ closest_match = get_close_matches(current_page, self.jinjia_prerender_page.keys(), n=1, cutoff=0)
66
+ return self.jinjia_prerender_page[closest_match[0]]
67
 
68
  def _generate_system_prompt(self):
69
  """Create a dynamic system prompt based on the current state."""
70
+ current_page = self.page_history[-1] if len(self.page_history) >= 1 else self.sitemap['pages'][0]['id']
71
+ last_page = self.page_history[-2] if len(self.page_history) > 1 else self.sitemap['pages'][0]['id']
72
+ relevant_database = self._get_relevant_data(current_page)
73
+ relevant_sitemap = next((page for page in self.sitemap["pages"] if page["id"] == current_page), self.sitemap["pages"])
74
+ prerender_page = self._get_prerender_page(current_page)
75
 
76
  return get_system_prompt(app_name=self.app_name,
77
+ app_description=self.app_description,
78
+ relevant_database=relevant_database,
79
  task=self.task,
 
80
  current_page=current_page,
81
  last_page=last_page,
82
  actions=self.actions,
83
+ sitemap_page=relevant_sitemap,
84
+ jinjia_prerender=prerender_page,
 
85
  )
86
 
87
  @retry(
 
117
  def one_conversation_round(self, user_input):
118
  """Conduct one round of conversation between the user and the assistant."""
119
  # User provides input
120
+ self.trajectory.append({"role": "user", "content": f'Human: {user_input}'})
121
  valid_input = self._is_valid_input(user_input)
122
  if valid_input[0]:
123
  pass
124
  else:
125
+ self.prompt_count += 1
126
+ invalid_input_message = f"\n{self.app_name}: Invalid input. {valid_input[1]}"
127
+ self.trajectory.append({"role": "assistant", "content": invalid_input_message})
128
+ return invalid_input_message
129
 
130
  self.actions.append(user_input + f'on {self.user_state["current_page"]} page')
131
  self.conversation.append({"role": "user", "content": user_input})
132
  self.prompt_count += 1
133
 
134
  # Update user state using GPT's response
135
+ current_page = self.page_history[-1] if len(self.page_history) >= 1 else self.sitemap['pages'][0]['id']
136
  update_prompt = get_user_state_update_prompt(user_input=user_input,
137
  current_page=current_page,
138
  task=self.task,
139
+ database=self.database,
140
  solution=self.solution,
141
  user_state=self.user_state,
142
  sitemap=self.sitemap)
143
 
144
+ self.conversation.append({"role": "user", "content": update_prompt})
145
  updated_state = self._get_openai_response(self.conversation).split("UPDATED", 1)[1].strip()
146
+ self.conversation.pop(-1) # update prompt don't have to stay in conversation history
147
 
148
  # Parse and update the user state
149
  updated_state = json_repair.loads(updated_state)
 
173
 
174
  try:
175
  if str(updated_state['task_completed']).lower() == 'true':
176
+ complete_message = f"{self.app_name}: Task completed! You took {self.prompt_count} steps."
177
+ self.trajectory.append({"role": "assistant", "content": complete_message})
178
+ return complete_message
179
  except:
180
  updated_state['task_completed'] = 'False'
181
 
 
192
  self.conversation.append({"role": "system", "content": system_prompt})
193
  gpt_instruction = self._get_openai_response(self.conversation)
194
  self.conversation.append({"role": "assistant", "content": gpt_instruction})
195
+ self.trajectory.append({"role": "assistant", "content": gpt_instruction})
196
  return gpt_instruction
197
 
198
  def start_conversation(self):
prompts.py CHANGED
@@ -1,97 +1,80 @@
 
1
 
2
- def get_system_prompt(app_name, system_data, task, user_data, current_page, last_page, actions, user_state, page_info, page_linkage):
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
- Current page links to these pages:
16
- {page_linkage}
17
-
18
- Each button on the page has a `name` and an `action_type`.
19
-
20
- ### Instruction Format:
21
- <If `actions` is non-empty: "You have successfully performed actions[-1]"> z
22
- <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']>
23
- <If current_page == log in & user_state['logged_in'] == True: You have successfully logged in as user_data['credentials']['user_id']>
24
- You are currently on the **{current_page}** page.
25
-
26
- <Display static information to users based on system data and user data on the current page (e.g., display search results)>
27
-
28
- You have the following options:
29
- 1. option 1: action type
30
- 2. option 2: action type
31
- 3. option 3: action type
32
- 4. option 4: action type
33
- <If any linked page does not have button links to it, add the button.>
34
- 5. Back to last page: click
35
- 6. Back to Home page: click
36
- 7. <If user_state['logged_in'] == False, display Log In: click to take user to log in page>
37
- 8. Do nothing: noop
38
-
39
- Each button is in the format: `number. button name: action_type`.
40
- Please provide your choice in the format: `action_type(button name)`.
41
- If you have a query, use the format: `type(button name, query)`.
42
- Please provide only one choice per turn.
43
-
44
- ### **Rules**:
45
- 1. Display all available options from the buttons on the page.
46
- 2. Remain robotic and emotionless. Avoid offering any advice or opinions to the user.
47
- 3. Ensure there is always a button to navigate back to the **{last_page}** and a button to go to the **Home page**.
48
- 4. Ensure there is always a button that allow user to do nothing.
49
- 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.
50
- 6. Group similar action types under ‘type’ and display them as ‘type’ (e.g., display ‘input’ or ‘search’ as ‘type’).
51
- 7. Mimic the real {app_name} scenario as much as possible.
52
- """
53
  return system_prompt
54
 
55
- def get_user_state_update_prompt(user_input, current_page, task, solution, user_state, sitemap):
56
- structure = """
57
- {
58
- 'current_page': 'Home',
59
- 'task_completed': False,
60
- 'back': False
61
- }
62
- """
63
- update_prompt = f"""
64
- You will now update the user state.
65
- The user takes the action '{user_input}' on the {current_page} page. Determine which page the user will move to next.
66
- The next page must from the sitemap: {sitemap}
67
- Recall the user's task: {task}. Solution to the user's task: {solution}.
68
- Update the `user_state` dictionary based on the user's last action:
69
- Current user_state: {user_state}.
70
-
71
- ### Instructions:
72
- 1. If the 'current_page' has changed, update it to a valid page from the sitemap.
73
- 2. If the task is completed, update 'task_completed' to `True`. Otherwise, leave it as `False`.
74
- 3. If no updates are needed, return the `user_state` exactly as provided, without any changes.
75
-
76
- ### Important Notes:
77
- - Ensure 'current_page' and 'task_completed' are always present as keys in the returned dictionary.
78
- - If the user go back a page from current page, set 'back' to `True`. Otherwise, it should remain `False`.
79
- - Return only the updated dictionary, without additional text, explanations, or wrapping.
80
-
81
- ### Output Format:
82
- 1. Start with "REASON": Explain which fields in `user_state` need to be updated and why.
83
- 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.
84
- """
 
 
 
 
 
 
 
 
85
  return update_prompt
86
 
87
 
88
  def get_agent_prompt(app_name, task, conversation):
89
- agent_prompt = f"""
90
  Imagine you are an agent navigate through the {app_name} environment.
91
  Your overarching task is: {task}. You may have done some part of the task, or none at all.
92
  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.
93
  The last message from the assistant was: {conversation[-1]['content']}
94
  Respond first with a brief "Plan" which suggests what steps you are going to take to accomplish the task, and what your immediate.
95
  Then generate an "Action" which is the immediate next step you can take.
96
- """
97
  return agent_prompt
 
1
+ import textwrap
2
 
3
+
4
+ def get_system_prompt(app_name, app_description, relevant_database, task,
5
+ current_page, last_page, actions, sitemap_page, jinjia_prerender):
6
+ system_prompt = textwrap.dedent(f"""
7
+ You are a text-based CLI {app_name} ({app_description}) simulator.
8
+ The {app_name} app contains the following database tables: {relevant_database}.
9
+ You are interacting with a user whose task is: {task}.
10
+ The user's last page was {last_page}, and they have taken the following actions: {actions}.
11
+ After performing the most recent action, the user is now on the {current_page} page.
12
+
13
+ Details and buttons on the current page:
14
+ {sitemap_page}
15
+
16
+ This page should display following pre-rendered page to user and making sure replace the placeholders with real data from database.
17
+
18
+ {jinjia_prerender}
19
+
20
+ ### **Rules**:
21
+ 1. Display all information from the pre-rendered page.
22
+ 2. Remain robotic and emotionless. Avoid offering any advice or opinions to the user.
23
+ 3. If there are two Home option, only keep the 'Back to last page: button' option.
24
+ 4. Add a button to interact with displayed item if such button doesn't exist.
25
+ 5. Mimic the real {app_name} scenario as much as possible.
26
+ 6. Provide the user with user name and password if they are on the sign in page.
27
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  return system_prompt
29
 
30
+
31
+ def get_database_update_prompt(user_input, current_page, database):
32
+ update_database = textwrap.dedent(f"""
33
+ You will now update the database.
34
+ The user takes the action '{user_input}' on the {current_page} page. Determine how will database change.
35
+ Recall the current Database:
36
+ {database}
37
+
38
+ ### Output Format:
39
+ 1. Start with "REASON": Explain which fields in database need to be updated and why.
40
+ 2. End with "UPDATED": Provide only the updated database. Follow the input database schema structure exactly without modifying its organization. Do not include explanations or any additional text after updated database.
41
+ """)
42
+ return update_database
43
+
44
+
45
+ def get_user_state_update_prompt(user_input, current_page, task, database, solution, user_state, sitemap):
46
+ update_prompt = textwrap.dedent(f"""
47
+ You will now update the user state.
48
+ The user takes the action '{user_input}' on the {current_page} page. Determine which page the user will move to next.
49
+ The next page must from the sitemap: {sitemap}
50
+ Recall the user's task: {task}. Recall the current Database: {database}. Solution to the user's task: {solution}.
51
+ Update the `user_state` dictionary based on the user's last action:
52
+ Current user_state: {user_state}.
53
+
54
+ ### Instructions:
55
+ 1. If the 'current_page' has changed, update it to a valid page from the sitemap.
56
+ 2. If the task is completed, update 'task_completed' to `True`. Otherwise, leave it as `False`.
57
+ 3. If no updates are needed, return the `user_state` exactly as provided, without any changes.
58
+
59
+ ### Important Notes:
60
+ - Ensure 'current_page' and 'task_completed' are always present as keys in the returned dictionary.
61
+ - If the user go back a page from current page, set 'back' to `True`. Otherwise, it should remain `False`.
62
+ - Return only the updated dictionary, without additional text, explanations, or wrapping.
63
+
64
+ ### Output Format:
65
+ 1. Start with "REASON": Explain which fields in `user_state` need to be updated and why.
66
+ 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.
67
+ """)
68
  return update_prompt
69
 
70
 
71
  def get_agent_prompt(app_name, task, conversation):
72
+ agent_prompt = textwrap.dedent(f"""
73
  Imagine you are an agent navigate through the {app_name} environment.
74
  Your overarching task is: {task}. You may have done some part of the task, or none at all.
75
  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.
76
  The last message from the assistant was: {conversation[-1]['content']}
77
  Respond first with a brief "Plan" which suggests what steps you are going to take to accomplish the task, and what your immediate.
78
  Then generate an "Action" which is the immediate next step you can take.
79
+ """)
80
  return agent_prompt