File size: 4,658 Bytes
09321b6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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