Spaces:
Runtime error
Runtime error
| import json | |
| import copy | |
| import shutil | |
| from jupyter_backend import * | |
| from tools import * | |
| from typing import * | |
| from notebook_serializer import add_markdown_to_notebook, add_code_cell_to_notebook | |
| functions = [ | |
| { | |
| "name": "execute_code", | |
| "description": "This function allows you to execute Python code and retrieve the terminal output. If the code " | |
| "generates image output, the function will return the text '[image]'. The code is sent to a " | |
| "Jupyter kernel for execution. The kernel will remain active after execution, retaining all " | |
| "variables in memory.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "code": { | |
| "type": "string", | |
| "description": "The code text" | |
| } | |
| }, | |
| "required": ["code"], | |
| } | |
| }, | |
| ] | |
| system_msg = '''You are an AI code interpreter. | |
| Your goal is to help users do a variety of jobs by executing Python code. | |
| You should: | |
| 1. Comprehend the user's requirements carefully & to the letter. | |
| 2. Give a brief description for what you plan to do & call the provided function to run code. | |
| 3. Provide results analysis based on the execution output. | |
| 4. If error occurred, try to fix it. | |
| 5. Response in the same language as the user. | |
| Note: If the user uploads a file, you will receive a system message "User uploaded a file: filename". Use the filename as the path in the code. ''' | |
| with open('config.json') as f: | |
| config = json.load(f) | |
| if not config['API_KEY']: | |
| config['API_KEY'] = os.getenv('OPENAI_API_KEY') | |
| os.unsetenv('OPENAI_API_KEY') | |
| def get_config(): | |
| return config | |
| def config_openai_api(api_type, api_base, api_version, api_key): | |
| openai.api_type = api_type | |
| openai.api_base = api_base | |
| openai.api_version = api_version | |
| openai.api_key = api_key | |
| class GPTResponseLog: | |
| def __init__(self): | |
| self.assistant_role_name = '' | |
| self.content = '' | |
| self.function_name = None | |
| self.function_args_str = '' | |
| self.code_str = '' | |
| self.display_code_block = '' | |
| self.finish_reason = 'stop' | |
| self.bot_history = None | |
| self.stop_generating = False | |
| self.code_executing = False | |
| self.interrupt_signal_sent = False | |
| def reset_gpt_response_log_values(self, exclude=None): | |
| if exclude is None: | |
| exclude = [] | |
| attributes = {'assistant_role_name': '', | |
| 'content': '', | |
| 'function_name': None, | |
| 'function_args_str': '', | |
| 'code_str': '', | |
| 'display_code_block': '', | |
| 'finish_reason': 'stop', | |
| 'bot_history': None, | |
| 'stop_generating': False, | |
| 'code_executing': False, | |
| 'interrupt_signal_sent': False} | |
| for attr_name in exclude: | |
| del attributes[attr_name] | |
| for attr_name, value in attributes.items(): | |
| setattr(self, attr_name, value) | |
| def set_assistant_role_name(self, assistant_role_name: str): | |
| self.assistant_role_name = assistant_role_name | |
| def add_content(self, content: str): | |
| self.content += content | |
| def set_function_name(self, function_name: str): | |
| self.function_name = function_name | |
| def copy_current_bot_history(self, bot_history: List): | |
| self.bot_history = copy.deepcopy(bot_history) | |
| def add_function_args_str(self, function_args_str: str): | |
| self.function_args_str += function_args_str | |
| def update_code_str(self, code_str: str): | |
| self.code_str = code_str | |
| def update_display_code_block(self, display_code_block): | |
| self.display_code_block = display_code_block | |
| def update_finish_reason(self, finish_reason: str): | |
| self.finish_reason = finish_reason | |
| def update_stop_generating_state(self, stop_generating: bool): | |
| self.stop_generating = stop_generating | |
| def update_code_executing_state(self, code_executing: bool): | |
| self.code_executing = code_executing | |
| def update_interrupt_signal_sent(self, interrupt_signal_sent: bool): | |
| self.interrupt_signal_sent = interrupt_signal_sent | |
| class BotBackend(GPTResponseLog): | |
| def __init__(self): | |
| super().__init__() | |
| self.unique_id = hash(id(self)) | |
| self.jupyter_work_dir = f'cache/work_dir_{self.unique_id}' | |
| self.tool_log = f'cache/tool_{self.unique_id}.log' | |
| self.jupyter_kernel = JupyterKernel(work_dir=self.jupyter_work_dir) | |
| self.gpt_model_choice = "GPT-3.5" | |
| self.revocable_files = [] | |
| self.system_msg = system_msg | |
| self.functions = copy.deepcopy(functions) | |
| self._init_api_config() | |
| self._init_tools() | |
| self._init_conversation() | |
| self._init_kwargs_for_chat_completion() | |
| def _init_conversation(self): | |
| first_system_msg = {'role': 'system', 'content': self.system_msg} | |
| self.context_window_tokens = 0 # num of tokens actually sent to GPT | |
| self.sliced = False # whether the conversion is sliced | |
| if hasattr(self, 'conversation'): | |
| self.conversation.clear() | |
| self.conversation.append(first_system_msg) | |
| else: | |
| self.conversation: List[Dict] = [first_system_msg] | |
| def _init_api_config(self): | |
| self.config = get_config() | |
| api_type = self.config['API_TYPE'] | |
| api_base = self.config['API_base'] | |
| api_version = self.config['API_VERSION'] | |
| api_key = config['API_KEY'] | |
| config_openai_api(api_type, api_base, api_version, api_key) | |
| def _init_tools(self): | |
| self.additional_tools = {} | |
| tool_datas = get_available_tools(self.config) | |
| if tool_datas: | |
| self.system_msg += '\n\nAdditional tools:' | |
| for tool_data in tool_datas: | |
| system_prompt = tool_data['system_prompt'] | |
| tool_name = tool_data['tool_name'] | |
| tool_description = tool_data['tool_description'] | |
| self.system_msg += f'\n{tool_name}: {system_prompt}' | |
| self.functions.append(tool_description) | |
| self.additional_tools[tool_name] = { | |
| 'tool': tool_data['tool'], | |
| 'additional_parameters': copy.deepcopy(tool_data['additional_parameters']) | |
| } | |
| for parameter, value in self.additional_tools[tool_name]['additional_parameters'].items(): | |
| if callable(value): | |
| self.additional_tools[tool_name]['additional_parameters'][parameter] = value(self) | |
| def _init_kwargs_for_chat_completion(self): | |
| self.kwargs_for_chat_completion = { | |
| 'stream': True, | |
| 'messages': self.conversation, | |
| 'functions': self.functions, | |
| 'function_call': 'auto' | |
| } | |
| model_name = self.config['model'][self.gpt_model_choice]['model_name'] | |
| if self.config['API_TYPE'] == 'azure': | |
| self.kwargs_for_chat_completion['engine'] = model_name | |
| else: | |
| self.kwargs_for_chat_completion['model'] = model_name | |
| def _backup_all_files_in_work_dir(self): | |
| count = 1 | |
| backup_dir = f'cache/backup_{self.unique_id}' | |
| while os.path.exists(backup_dir): | |
| count += 1 | |
| backup_dir = f'cache/backup_{self.unique_id}_{count}' | |
| shutil.copytree(src=self.jupyter_work_dir, dst=backup_dir) | |
| def _clear_all_files_in_work_dir(self, backup=True): | |
| if backup: | |
| self._backup_all_files_in_work_dir() | |
| for filename in os.listdir(self.jupyter_work_dir): | |
| path = os.path.join(self.jupyter_work_dir, filename) | |
| if os.path.isdir(path): | |
| shutil.rmtree(path) | |
| else: | |
| os.remove(path) | |
| def _save_tool_log(self, tool_response): | |
| with open(self.tool_log, 'a', encoding='utf-8') as log_file: | |
| log_file.write(f'Previous conversion: {self.conversation}\n') | |
| log_file.write(f'Model choice: {self.gpt_model_choice}\n') | |
| log_file.write(f'Tool name: {self.function_name}\n') | |
| log_file.write(f'Parameters: {self.function_args_str}\n') | |
| log_file.write(f'Response: {tool_response}\n') | |
| log_file.write('----------\n\n') | |
| def add_gpt_response_content_message(self): | |
| self.conversation.append( | |
| {'role': self.assistant_role_name, 'content': self.content} | |
| ) | |
| add_markdown_to_notebook(self.content, title="Assistant") | |
| def add_text_message(self, user_text): | |
| self.conversation.append( | |
| {'role': 'user', 'content': user_text} | |
| ) | |
| self.revocable_files.clear() | |
| self.update_finish_reason(finish_reason='new_input') | |
| add_markdown_to_notebook(user_text, title="User") | |
| def add_file_message(self, path, bot_msg): | |
| filename = os.path.basename(path) | |
| work_dir = self.jupyter_work_dir | |
| shutil.copy(path, work_dir) | |
| gpt_msg = {'role': 'system', 'content': f'User uploaded a file: {filename}'} | |
| self.conversation.append(gpt_msg) | |
| self.revocable_files.append( | |
| { | |
| 'bot_msg': bot_msg, | |
| 'gpt_msg': gpt_msg, | |
| 'path': os.path.join(work_dir, filename) | |
| } | |
| ) | |
| def add_function_call_response_message(self, function_response: Union[str, None], save_tokens=True): | |
| if self.code_str is not None: | |
| add_code_cell_to_notebook(self.code_str) | |
| self.conversation.append( | |
| { | |
| "role": self.assistant_role_name, | |
| "name": self.function_name, | |
| "content": self.function_args_str | |
| } | |
| ) | |
| if function_response is not None: | |
| if save_tokens and len(function_response) > 500: | |
| function_response = f'{function_response[:200]}\n[Output too much, the middle part output is omitted]\n ' \ | |
| f'End part of output:\n{function_response[-200:]}' | |
| self.conversation.append( | |
| { | |
| "role": "function", | |
| "name": self.function_name, | |
| "content": function_response, | |
| } | |
| ) | |
| self._save_tool_log(tool_response=function_response) | |
| def append_system_msg(self, prompt): | |
| self.conversation.append( | |
| {'role': 'system', 'content': prompt} | |
| ) | |
| def revoke_file(self): | |
| if self.revocable_files: | |
| file = self.revocable_files[-1] | |
| bot_msg = file['bot_msg'] | |
| gpt_msg = file['gpt_msg'] | |
| path = file['path'] | |
| assert self.conversation[-1] is gpt_msg | |
| del self.conversation[-1] | |
| os.remove(path) | |
| del self.revocable_files[-1] | |
| return bot_msg | |
| else: | |
| return None | |
| def update_gpt_model_choice(self, model_choice): | |
| self.gpt_model_choice = model_choice | |
| self._init_kwargs_for_chat_completion() | |
| def update_token_count(self, num_tokens): | |
| self.__setattr__('context_window_tokens', num_tokens) | |
| def update_sliced_state(self, sliced): | |
| self.__setattr__('sliced', sliced) | |
| def send_interrupt_signal(self): | |
| self.jupyter_kernel.send_interrupt_signal() | |
| self.update_interrupt_signal_sent(interrupt_signal_sent=True) | |
| def restart(self): | |
| self.revocable_files.clear() | |
| self._init_conversation() | |
| self.reset_gpt_response_log_values() | |
| self.jupyter_kernel.restart_jupyter_kernel() | |
| self._clear_all_files_in_work_dir() | |