Spaces:
Sleeping
Sleeping
File size: 8,531 Bytes
2649124 54d66e1 4125626 2649124 41f933e 2649124 a1c328e 3f815a2 2649124 54d66e1 2649124 a1c328e 2649124 a1c328e 54d66e1 a1c328e 2649124 33304fc 2649124 33304fc 2649124 15af633 2649124 33304fc 2649124 e3479f5 2649124 54d66e1 2649124 54d66e1 15af633 2649124 54d66e1 2649124 3a8cc44 0342ce4 2649124 a1c328e 15af633 258c0f5 3f815a2 258c0f5 15af633 258c0f5 15af633 258c0f5 a1c328e 3f815a2 258c0f5 3f815a2 2649124 dbdae97 2649124 4125626 e3ac915 56e14a4 e3ac915 29ff717 15af633 3f815a2 ecf6658 15af633 3f815a2 2649124 54d66e1 2649124 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
from openai import OpenAI
import json_repair
from transformers import AutoTokenizer
from openai import RateLimitError
import time
class ChatbotSimulation:
def __init__(self, site_map, page_details, user_state, task,
app_name, log_location, openai_api_key, agent='human',
max_steps=50, max_tokens=8192, buffer_tokens=500):
self.sitemap = site_map
self.page_details = page_details
self.user_state = user_state
self.user_state['current_page'] = 'Home' # Initialize current page
self.user_state['last_page'] = 'Home'
self.user_state['task_completed'] = 'False'
self.task = task
self.app_name = app_name
self.log_location = log_location
self.agent = agent.lower()
if self.agent not in ['human', 'llm']:
raise ValueError("Invalid agent type. Expected 'Human' or 'llm'.")
self.max_steps = max_steps
self.max_tokens = max_tokens
self.buffer_tokens = buffer_tokens
self.conversation = [] # Stores recent conversation snippets
self.prompt_count = 0
self.client = OpenAI(api_key=openai_api_key)
self.actions = []
self.tokenizer = AutoTokenizer.from_pretrained("gpt2")
def _get_page_uid(self, page_name):
"""Retrieve the UID of the given page from the sitemap."""
return self.sitemap['pages'].get(page_name, {}).get('uid')
def _get_page_details(self, page_name):
"""Retrieve the page details using its UID."""
uid = self._get_page_uid(page_name)
return self.page_details.get(uid, {})
def _generate_system_prompt(self):
"""Create a dynamic system prompt based on the current state."""
current_page = self.user_state['current_page']
last_page = self.user_state['last_page']
page_info = self._get_page_details(current_page)
return f"""
You are a text-based simulator of {self.app_name} app.
You are interacting with a user. User's task is: {self.task}.
User's last page was {last_page} and the user have taken actions: {self.actions}.
After action, user is currently on the {current_page} page.
Current user state: {self.user_state}.
Page Information:
- **If the user requests page you do not possess** (such as a list of restaurants, menus, or similar details),
you are permitted to create plausible and relevant information to fulfill the request.
Present this fabricated information convincingly as if it were real data.
{page_info}
- **Features**: Represent available options the user can select on this page.
- **User Data**: Represents user-specific data accessible on this page.
Provide instructions or request input from the user. If the user provides an invalid action, respond with:
"Invalid action. Please select a valid option."
### Instruction Format:
<if actions is non-empty: You have successfully done actions[-1]> You are at the {current_page} page. You have the following options:
1. Feature 1
2. Feature 2
3. Feature 3
4. Feature 4
Please enter your choice as 'Number. Description'. If you have a query, enter as 'Number. Description: query'
Rules:
- Be sure to display all options that is available in features.
- Be robotic and emotionless. Avoid offering any advice to the user.
"""
def _get_openai_response(self, prompt):
"""Fetch response from OpenAI API."""
self._trim_conversation()
while True:
try:
response = self.client.chat.completions.create(
model="gpt-4",
messages=prompt,
max_tokens=self.buffer_tokens, # Adjusted max_tokens if needed
temperature=0.7,
)
return response.choices[0].message.content
except RateLimitError as e:
# Parse the suggested retry time from the error message, default to 5s if not available
wait_time = 5
try:
# Attempt to get the time from the error message
wait_time = float(e.response['error']['message'].split("in ")[1].split("s")[0])
except (KeyError, IndexError, ValueError):
print("Could not parse wait time from error message. Defaulting to 5 seconds.")
print(f"Rate limit reached. Retrying in {wait_time} seconds...")
time.sleep(wait_time)
def _calculate_token_count(self, conversation):
"""Accurately calculate the token count in the conversation using a tokenizer."""
total_tokens = 0
for entry in conversation:
# Tokenize each entry content and count tokens
tokens = self.tokenizer.encode(entry['content'], truncation=False, add_special_tokens=False)
total_tokens += len(tokens)
return total_tokens
def _trim_conversation(self):
"""Trim the conversation to keep it within the token limit."""
while self._calculate_token_count(self.conversation) >= (self.max_tokens - self.buffer_tokens * 2):
self.conversation.pop(0)
def one_conversation_round(self, user_input):
"""Conduct one round of conversation between the user and the assistant."""
# User provides input
self.actions.append(user_input + f'on {self.user_state["current_page"]} page')
self.conversation.append({"role": "user", "content": user_input})
self.prompt_count += 1
# Update user state using GPT's response
update_prompt = f"""
If user takes action '{user_input}' on {self.user_state['current_page']} page, which page will they move to?
Recall user's task: {self.task}
Update the user_state dictionary based on user's last action:
Current user_state: {self.user_state}
Sitemap: {self.sitemap}
Instructions:
1. If the 'current_page' has changed, update it to a page from the sitemap.
2. If the task is finished, update 'task_completed' to True. Otherwise, leave it as False.
3. If no updates are needed, return the user state exactly as provided, without modification.
Important:
- Ensure 'current_page' and 'task_completed' are keys in the returned dictionary.
- Return only the dictionary without additional output or wrapping.
Example Output Format:
{{
'current_page': 'Home',
'last_page': 'Home',
'task_completed': 'False',
}}
"""
self.conversation.append({"role": "assistant", "content": update_prompt})
updated_state = self._get_openai_response(self.conversation)
self.conversation.pop(-1)
# Parse and update the user state
updated_state = json_repair.loads(updated_state)
required_keys = {'current_page', 'last_page', 'task_completed'}
while not isinstance(updated_state, dict) or not required_keys.issubset(updated_state.keys()):
transform_prompt = f""""
Transform {updated_state} to a properly formate JSON file.
Example Output Format:
{{
'current_page': 'Home',
'last_page': 'Home',
'task_completed': 'False',
}}
"""
updated_state = self._get_openai_response([{"role": "system", "content": transform_prompt}])
updated_state = json_repair.loads(updated_state)
try:
if updated_state['task_completed'].lower() == 'true':
return f"Task completed! You took {self.prompt_count} steps."
except:
updated_state['task_completed'] = 'False'
self.user_state = updated_state
#self.conversation.clear()
system_prompt = self._generate_system_prompt()
# GPT generates the page instructions
self.conversation.append({"role": "system", "content": system_prompt})
gpt_instruction = self._get_openai_response(self.conversation)
self.conversation.append({"role": "assistant", "content": gpt_instruction})
return gpt_instruction
def start_conversation(self):
greeting = f'\n Welcome to {self.app_name} simulator! Your task is: {self.task}. \n'
system_prompt = self._generate_system_prompt()
# GPT generates the page instructions
self.conversation.append({"role": "system", "content": system_prompt})
gpt_instruction = self._get_openai_response(self.conversation)
self.conversation.append({"role": "assistant", "content": gpt_instruction})
return greeting + gpt_instruction
|