import os import re import traceback import appdirs import json from .code_interpreter_utils.create_code_interpreter import \ create_code_interpreter from .code_interpreter_utils.language_map import language_map from .code_interpreter_utils.truncate_output import truncate_output from .tool import Tool class CodeInterpreter(Tool): """ using open interpreter to interpret code by https://github.com/KillianLucas/open-interpreter """ description = 'Executes code on the user\'s machine, **in the users local environment**, and returns the output' name = 'code_interpreter' parameters: list = [{ 'name': 'language', 'description': 'The programming language (required parameter to the `execute` function)', 'required': True }, { 'name': 'code', 'description': 'The code to execute (required)', 'required': True }] def __init__(self, cfg={}): super().__init__(cfg) self.create_code_interpreter = create_code_interpreter self.language_map = language_map self.truncate_output = truncate_output self._code_interpreters = {} self.max_output = self.cfg.get('max_output', 2000) def _local_call(self, *args, **kwargs): language, code = self._handle_input_fallback(**kwargs) try: # Fix a common error where the LLM thinks it's in a Jupyter notebook if language == 'python' and code.startswith('!'): code = code[1:] language = 'shell' if language in self.language_map: if language not in self._code_interpreters: self._code_interpreters[ language] = self.create_code_interpreter(language) code_interpreter = self._code_interpreters[language] else: # This still prints code but don't allow code to run. Let Open-Interpreter know through output message error_output = f'Error: Open Interpreter does not currently support {language}.' print(error_output) output = '\n' + error_output return {'result': output.strip()} output = '' for line in code_interpreter.run(code): if 'output' in line: output += '\n' + line['output'] # Truncate output output = self.truncate_output(output, self.max_output) except Exception as e: error = traceback.format_exc() output = ' '.join(f'{key}:{value}' for key, value in kwargs.items()) output += f'\nDetail error is {e}.\n{error}' return {'result': output.strip()} def _handle_input_fallback(self, **kwargs): """ an alternative method is to parse code in content not from function call such as: text = response['content'] code_block = re.search(r'```([\s\S]+)```', text) # noqa W^05 if code_block: result = code_block.group(1) language = result.split('\n')[0] code = '\n'.join(result.split('\n')[1:]) :param fallback_text: :return: language, cocde """ language = kwargs.get('language', None) code = kwargs.get('code', None) fallback = kwargs.get('fallback', None) if language and code: return language, code elif fallback: try: text = fallback code_block = re.search(r'```([\s\S]+)```', text) # noqa W^05 if code_block: result = code_block.group(1) # for multi code_block result = result.split('```')[0] language = result.split('\n')[0] if language == 'py' or language == 'python': # handle py case # ```py code ``` language = 'python' code = '\n'.join(result.split('\n')[1:]) return language, code if language == 'json': # handle json case # ```json {language,code}``` parameters = json.loads('\n'.join( result.split('\n')[1:]).replace('\n', '')) return parameters['language'], parameters['code'] except ValueError: return language, code else: return language, code