File size: 11,889 Bytes
886d8e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# `0.2.0` Migration Guide

Open Interpreter is [changing](https://changes.openinterpreter.com/log/the-new-computer-update). This guide will help you migrate your application to `0.2.0`, also called the _New Computer Update_ (NCU), the latest major version of Open Interpreter.

## A New Start

To start using Open Interpreter in Python, we now use a standard **class instantiation** format:

```python
# From the module `interpreter`, import the class `OpenInterpreter`
from interpreter import OpenInterpreter

# Create an instance of `OpenInterpreter` to use it
agent = OpenInterpreter()
agent.chat()
```

For convenience, we also provide an instance of `interpreter`, which you can import from the module (also called `interpreter`):

```python
 # From the module `interpreter`, import the included instance of `OpenInterpreter`
from interpreter import interpreter

interpreter.chat()
```

## New Parameters

All stateless LLM attributes have been moved to `interpreter.llm`:

- `interpreter.model` β†’ `interpreter.llm.model`
- `interpreter.api_key` β†’ `interpreter.llm.api_key`
- `interpreter.llm_supports_vision` β†’ `interpreter.llm.supports_vision`
- `interpreter.supports_function_calling` β†’ `interpreter.llm.supports_functions`
- `interpreter.max_tokens` β†’ `interpreter.llm.max_tokens`
- `interpreter.context_window` β†’ `interpreter.llm.context_window`
- `interpreter.temperature` β†’ `interpreter.llm.temperature`
- `interpreter.api_version` β†’ `interpreter.llm.api_version`
- `interpreter.api_base` β†’ `interpreter.llm.api_base`

This is reflected **1)** in Python applications using Open Interpreter and **2)** in your profile for OI's terminal interface, which can be edited via `interpreter --profiles`.

## New Static Messages Structure

- The array of messages is now flat, making the architecture more modular, and easier to adapt to new kinds of media in the future.
- Each message holds only one kind of data. This yields more messages, but prevents large nested messages that can be difficult to parse.
- This allows you to pass the full `messages` list into Open Interpreter as `interpreter.messages = message_list`.
- Every message has a "role", which can be "assistant", "computer", or "user".
- Every message has a "type", specifying the type of data it contains.
- Every message has "content", which contains the data for the message.
- Some messages have a "format" key, to specify the format of the content, like "path" or "base64.png".
- The recipient of the message is specified by the "recipient" key, which can be "user" or "assistant". This is used to inform the LLM of who the message is intended for.

```python
[
  {"role": "user", "type": "message", "content": "Please create a plot from this data and display it as an image and then as HTML."}, # implied format: text (only one format for type message)
  {"role": "user", "type": "image", "format": "path", "content": "path/to/image.png"}
  {"role": "user", "type": "file", "content": "/path/to/file.pdf"} # implied format: path (only one format for type file)
  {"role": "assistant", "type": "message", "content": "Processing your request to generate a plot."} # implied format: text
  {"role": "assistant", "type": "code", "format": "python", "content": "plot = create_plot_from_data('data')\ndisplay_as_image(plot)\ndisplay_as_html(plot)"}
  {"role": "computer", "type": "image", "format": "base64.png", "content": "base64"}
  {"role": "computer", "type": "code", "format": "html", "content": "<html>Plot in HTML format</html>"}
  {"role": "computer", "type": "console", "format": "output", "content": "{HTML errors}"}
  {"role": "assistant", "type": "message", "content": "Plot generated successfully."} # implied format: text
]
```

## New Streaming Structure

- The streaming data structure closely matches the static messages structure, with only a few differences.
- Every streaming chunk has a "start" and "end" key, which are booleans that specify whether the chunk is the first or last chunk in the stream. This is what you should use to build messages from the streaming chunks.
- There is a "confirmation" chunk type, which is used to confirm with the user that the code should be run. The "content" key of this chunk is a dictionary with a `code` and a `language` key.
- Introducing more information per chunk is helpful in processing the streaming responses. Please take a look below for example code for processing streaming responses, in JavaScript.

```python
{"role": "assistant", "type": "message", "start": True}
{"role": "assistant", "type": "message", "content": "Pro"}
{"role": "assistant", "type": "message", "content": "cessing"}
{"role": "assistant", "type": "message", "content": "your request"}
{"role": "assistant", "type": "message", "content": "to generate a plot."}
{"role": "assistant", "type": "message", "end": True}

{"role": "assistant", "type": "code", "format": "python", "start": True}
{"role": "assistant", "type": "code", "format": "python", "content": "plot = create_plot_from_data"}
{"role": "assistant", "type": "code", "format": "python", "content": "('data')\ndisplay_as_image(plot)"}
{"role": "assistant", "type": "code", "format": "python", "content": "\ndisplay_as_html(plot)"}
{"role": "assistant", "type": "code", "format": "python", "end": True}

# The computer will emit a confirmation chunk *before* running the code. You can break here to cancel the execution.

{"role": "computer", "type": "confirmation", "format": "execution", "content": {
    "type": "code",
    "format": "python",
    "content": "plot = create_plot_from_data('data')\ndisplay_as_image(plot)\ndisplay_as_html(plot)",
}}

{"role": "computer", "type": "console", "start": True}
{"role": "computer", "type": "console", "format": "output", "content": "a printed statement"}
{"role": "computer", "type": "console", "format": "active_line", "content": "1"}
{"role": "computer", "type": "console", "format": "active_line", "content": "2"}
{"role": "computer", "type": "console", "format": "active_line", "content": "3"}
{"role": "computer", "type": "console", "format": "output", "content": "another printed statement"}
{"role": "computer", "type": "console", "end": True}
```

## Tips and Best Practices

- Adding an `id` and a `created_at` field to messages can be helpful to manipulate the messages later on.
- If you want your application to run the code instead of OI, then your app will act as the `computer`. This means breaking from the stream once OI emits a confirmation chunk (`{'role': 'computer', 'type': 'confirmation' ...}`) to prevent OI from running the code. When you run code, grab the message history via `messages = interpreter.messages`, then simply mimic the `computer` format above by appending new `{'role': 'computer' ...}` messages, then run `interpreter.chat(messages)`.
- Open Interpreter is designed to stop code execution when the stream is disconnected. Use this to your advantage to add a "Stop" button to the UI.
- Setting up your Python server to send errors and exceptions to the client can be helpful for debugging and generating error messages.

## Example Code

### Types

Python:

```python
class Message:
    role: Union["user", "assistant", "computer"]
    type: Union["message", "code", "image", "console", "file", "confirmation"]
    format: Union["output", "path", "base64.png", "base64.jpeg", "python", "javascript", "shell", "html", "active_line", "execution"]
    recipient: Union["user", "assistant"]
    content: Union[str, dict]  # dict should have 'code' and 'language' keys, this is only for confirmation messages

class StreamingChunk(Message):
    start: bool
    end: bool
```

TypeScript:

```typescript
interface Message {
  role: "user" | "assistant" | "computer";
  type: "message" | "code" | "image" | "console" | "file", | "confirmation";
  format: "output" | "path" | "base64.png" | "base64.jpeg" | "python" | "javascript" | "shell" | "html" | "active_line", | "execution";
  recipient: "user" | "assistant";
  content: string | { code: string; language: string };
}
```

```typescript
interface StreamingChunk extends Message {
  start: boolean;
  end: boolean;
}
```

### Handling streaming chunks

Here is a minimal example of how to handle streaming chunks in JavaScript. This example assumes that you are using a Python server to handle the streaming requests, and that you are using a JavaScript client to send the requests and handle the responses. See the main repository README for an example FastAPI server.

```javascript
//Javascript

let messages = []; //variable to hold all messages
let currentMessageIndex = 0; //variable to keep track of the current message index
let isGenerating = false; //variable to stop the stream

// Function to send a POST request to the OI
async function sendRequest() {
  // Temporary message to hold the message that is being processed
  try {
    // Define parameters for the POST request, add at least the full messages array, but you may also consider adding any other OI parameters here, like auto_run, local, etc.
    const params = {
      messages,
    };

    //Define a controller to allow for aborting the request
    const controller = new AbortController();
    const { signal } = controller;

    // Send the POST request to your Python server endpoint
    const interpreterCall = await fetch("https://YOUR_ENDPOINT/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(params),
      signal,
    });

    // Throw an error if the request was not successful
    if (!interpreterCall.ok) {
      console.error("Interpreter didn't respond with 200 OK");
      return;
    }

    // Initialize a reader for the response body
    const reader = interpreterCall.body.getReader();

    isGenerating = true;
    while (true) {
      const { value, done } = await reader.read();

      // Break the loop if the stream is done
      if (done) {
        break;
      }
      // If isGenerating is set to false, cancel the reader and break the loop. This will halt the execution of the code run by OI as well
      if (!isGenerating) {
        await reader.cancel();
        controller.abort();
        break;
      }
      // Decode the stream and split it into lines
      const text = new TextDecoder().decode(value);
      const lines = text.split("\n");
      lines.pop(); // Remove last empty line

      // Process each line of the response
      for (const line of lines) {
        const chunk = JSON.parse(line);
        await processChunk(chunk);
      }
    }
    //Stream has completed here, so run any code that needs to be run after the stream has finished
    if (isGenerating) isGenerating = false;
  } catch (error) {
    console.error("An error occurred:", error);
  }
}

//Function to process each chunk of the stream, and create messages
function processChunk(chunk) {
  if (chunk.start) {
    const tempMessage = {};
    //add the new message's data to the tempMessage
    tempMessage.role = chunk.role;
    tempMessage.type = chunk.type;
    tempMessage.content = "";
    if (chunk.format) tempMessage.format = chunk.format;
    if (chunk.recipient) tempMessage.recipient = chunk.recipient;

    //add the new message to the messages array, and set the currentMessageIndex to the index of the new message
    messages.push(tempMessage);
    currentMessageIndex = messages.length - 1;
  }

  //Handle active lines for code blocks
  if (chunk.format === "active_line") {
    messages[currentMessageIndex].activeLine = chunk.content;
  } else if (chunk.end && chunk.type === "console") {
    messages[currentMessageIndex].activeLine = null;
  }

  //Add the content of the chunk to current message, avoiding adding the content of the active line
  if (chunk.content && chunk.format !== "active_line") {
    messages[currentMessageIndex].content += chunk.content;
  }
}
```