Skip to content

Commit 91091bb

Browse files
authored
Merge branch 'OpenInterpreter:main' into main
2 parents 6603be9 + 197d4bc commit 91091bb

File tree

15 files changed

+1578
-618
lines changed

15 files changed

+1578
-618
lines changed

.github/workflows/python-package.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: Build and Test
22

33
on:
44
push:
5-
branches: ["main"]
5+
branches: ["main", "development"]
66
pull_request:
7-
branches: ["main"]
7+
branches: ["main", "development"]
88

99
jobs:
1010
build:
@@ -25,11 +25,9 @@ jobs:
2525
curl -sSL https://install.python-poetry.org | python3 -
2626
- name: Install dependencies
2727
run: |
28-
# Update poetry to the latest version.
29-
poetry self update
3028
# Ensure dependencies are installed without relying on a lock file.
3129
poetry update
32-
poetry install
30+
poetry install -E server
3331
- name: Test with pytest
3432
run: |
3533
poetry run pytest -s -x -k test_

docs/server/usage.mdx

Lines changed: 197 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
---
2-
title: Server Usage
3-
---
1+
# Server Usage Guide
42

53
## Starting the Server
64

@@ -27,15 +25,29 @@ async_interpreter.server.run(port=8000) # Default port is 8000, but you can cus
2725
Connect to the WebSocket server at `ws://localhost:8000/`.
2826

2927
### Message Format
30-
Messages must follow the LMC format with start and end flags. For detailed specifications, see the [LMC messages documentation](https://docs.openinterpreter.com/protocols/lmc-messages).
28+
Open Interpreter uses an extended version of OpenAI's message format called [LMC messages](https://docs.openinterpreter.com/protocols/lmc-messages) that allow for rich, multi-part messages. **Messages must be sent between start and end flags.** Here's the basic structure:
3129

32-
Basic message structure:
3330
```json
34-
{"role": "user", "type": "message", "start": true}
31+
{"role": "user", "start": true}
3532
{"role": "user", "type": "message", "content": "Your message here"}
36-
{"role": "user", "type": "message", "end": true}
33+
{"role": "user", "end": true}
3734
```
3835

36+
### Multi-part Messages
37+
You can send complex messages with multiple components:
38+
39+
1. Start with `{"role": "user", "start": true}`
40+
2. Add various types of content (message, file, image, etc.)
41+
3. End with `{"role": "user", "end": true}`
42+
43+
### Content Types
44+
You can include various types of content in your messages:
45+
46+
- Text messages: `{"role": "user", "type": "message", "content": "Your text here"}`
47+
- File paths: `{"role": "user", "type": "file", "content": "path/to/file"}`
48+
- Images: `{"role": "user", "type": "image", "format": "path", "content": "path/to/photo"}`
49+
- Audio: `{"role": "user", "type": "audio", "format": "wav", "content": "path/to/audio.wav"}`
50+
3951
### Control Commands
4052
To control the server's behavior, send the following commands:
4153

@@ -51,7 +63,7 @@ To control the server's behavior, send the following commands:
5163
```
5264
This executes a generated code block and allows the agent to proceed.
5365

54-
**Important**: If `auto_run` is set to `False`, the agent will pause after generating code blocks. You must send the "go" command to continue execution.
66+
**Note**: If `auto_run` is set to `False`, the agent will pause after generating code blocks. You must send the "go" command to continue execution.
5567

5668
### Completion Status
5769
The server indicates completion with the following message:
@@ -67,8 +79,46 @@ If an error occurs, the server will send an error message in the following forma
6779
```
6880
Your client should be prepared to handle these error messages appropriately.
6981

70-
### Example WebSocket Interaction
71-
Here's a simple example demonstrating the WebSocket interaction:
82+
## Code Execution Review
83+
84+
After code blocks are executed, you'll receive a review message:
85+
86+
```json
87+
{
88+
"role": "assistant",
89+
"type": "review",
90+
"content": "Review of the executed code, including safety assessment and potential irreversible actions."
91+
}
92+
```
93+
94+
This review provides important information about the safety and potential impact of the executed code. Pay close attention to these messages, especially when dealing with operations that might have significant effects on your system.
95+
96+
The `content` field of the review message may have two possible formats:
97+
98+
1. If the code is deemed completely safe, the content will be exactly `"<SAFE>"`.
99+
2. Otherwise, it will contain an explanation of why the code might be unsafe or have irreversible effects.
100+
101+
Example of a safe code review:
102+
```json
103+
{
104+
"role": "assistant",
105+
"type": "review",
106+
"content": "<SAFE>"
107+
}
108+
```
109+
110+
Example of a potentially unsafe code review:
111+
```json
112+
{
113+
"role": "assistant",
114+
"type": "review",
115+
"content": "This code performs file deletion operations which are irreversible. Please review carefully before proceeding."
116+
}
117+
```
118+
119+
## Example WebSocket Interaction
120+
121+
Here's an example demonstrating the WebSocket interaction:
72122

73123
```python
74124
import websockets
@@ -77,21 +127,25 @@ import asyncio
77127

78128
async def websocket_interaction():
79129
async with websockets.connect("ws://localhost:8000/") as websocket:
80-
# Send a message
81-
await websocket.send(json.dumps({"role": "user", "type": "message", "start": True}))
82-
await websocket.send(json.dumps({"role": "user", "type": "message", "content": "What's 2 + 2?"}))
83-
await websocket.send(json.dumps({"role": "user", "type": "message", "end": True}))
130+
# Send a multi-part user message
131+
await websocket.send(json.dumps({"role": "user", "start": True}))
132+
await websocket.send(json.dumps({"role": "user", "type": "message", "content": "Analyze this image:"}))
133+
await websocket.send(json.dumps({"role": "user", "type": "image", "format": "path", "content": "path/to/image.jpg"}))
134+
await websocket.send(json.dumps({"role": "user", "end": True}))
84135

85136
# Receive and process messages
86137
while True:
87138
message = await websocket.recv()
88139
data = json.loads(message)
89140

90141
if data.get("type") == "message":
91-
print(data.get("content", ""), end="", flush=True)
142+
print(f"Assistant: {data.get('content', '')}")
143+
elif data.get("type") == "review":
144+
print(f"Code Review: {data.get('content')}")
92145
elif data.get("type") == "error":
93146
print(f"Error: {data.get('content')}")
94147
elif data == {"role": "assistant", "type": "status", "content": "complete"}:
148+
print("Interaction complete")
95149
break
96150

97151
asyncio.run(websocket_interaction())
@@ -122,31 +176,46 @@ Example:
122176
```python
123177
response = requests.get("http://localhost:8000/settings/custom_instructions")
124178
print(response.json())
125-
# Output: {"custom_instructions": "You only write react."}
179+
# Output: {"custom_instructions": "You only write Python code."}
126180
```
127181

128-
## Advanced Usage: Accessing the FastAPI App Directly
182+
## OpenAI-Compatible Endpoint
129183

130-
The FastAPI app is exposed at `async_interpreter.server.app`. This allows you to add custom routes or host the app using Uvicorn directly.
184+
The server provides an OpenAI-compatible endpoint at `/openai`. This allows you to use the server with any tool or library that's designed to work with the OpenAI API.
131185

132-
Example of adding a custom route and hosting with Uvicorn:
186+
### Chat Completions Endpoint
187+
188+
The chat completions endpoint is available at:
189+
190+
```
191+
[server_url]/openai/chat/completions
192+
```
193+
194+
To use this endpoint, set the `api_base` in your OpenAI client or configuration to `[server_url]/openai`. For example:
133195

134196
```python
135-
from interpreter import AsyncInterpreter
136-
from fastapi import FastAPI
137-
import uvicorn
197+
import openai
138198

139-
async_interpreter = AsyncInterpreter()
140-
app = async_interpreter.server.app
199+
openai.api_base = "http://localhost:8000/openai" # Replace with your server URL if different
200+
openai.api_key = "dummy" # The key is not used but required by the OpenAI library
141201

142-
@app.get("/custom")
143-
async def custom_route():
144-
return {"message": "This is a custom route"}
202+
response = openai.ChatCompletion.create(
203+
model="gpt-3.5-turbo", # This model name is ignored, but required
204+
messages=[
205+
{"role": "system", "content": "You are a helpful assistant."},
206+
{"role": "user", "content": "What's the capital of France?"}
207+
]
208+
)
145209

146-
if __name__ == "__main__":
147-
uvicorn.run(app, host="0.0.0.0", port=8000)
210+
print(response.choices[0].message['content'])
148211
```
149212

213+
Note that only the chat completions endpoint (`/chat/completions`) is implemented. Other OpenAI API endpoints are not available.
214+
215+
When using this endpoint:
216+
- The `model` parameter is required but ignored.
217+
- The `api_key` is required by the OpenAI library but not used by the server.
218+
150219
## Using Docker
151220

152221
You can also run the server using Docker. First, build the Docker image from the root of the repository:
@@ -163,9 +232,107 @@ docker run -p 8000:8000 open-interpreter
163232

164233
This will expose the server on port 8000 of your host machine.
165234

235+
## Acknowledgment Feature
236+
237+
When the `INTERPRETER_REQUIRE_ACKNOWLEDGE` environment variable is set to `"True"`, the server requires clients to acknowledge each message received. This feature ensures reliable message delivery in environments where network stability might be a concern.
238+
239+
### How it works
240+
241+
1. When this feature is enabled, each message sent by the server will include an `id` field.
242+
2. The client must send an acknowledgment message back to the server for each received message.
243+
3. The server will wait for this acknowledgment before sending the next message.
244+
245+
### Client Implementation
246+
247+
To implement this on the client side:
248+
249+
1. Check if each received message contains an `id` field.
250+
2. If an `id` is present, send an acknowledgment message back to the server.
251+
252+
Here's an example of how to handle this in your WebSocket client:
253+
254+
```python
255+
import json
256+
import websockets
257+
258+
async def handle_messages(websocket):
259+
async for message in websocket:
260+
data = json.loads(message)
261+
262+
# Process the message as usual
263+
print(f"Received: {data}")
264+
265+
# Check if the message has an ID that needs to be acknowledged
266+
if "id" in data:
267+
ack_message = {
268+
"ack": data["id"]
269+
}
270+
await websocket.send(json.dumps(ack_message))
271+
print(f"Sent acknowledgment for message {data['id']}")
272+
273+
async def main():
274+
uri = "ws://localhost:8000"
275+
async with websockets.connect(uri) as websocket:
276+
await handle_messages(websocket)
277+
278+
# Run the async function
279+
import asyncio
280+
asyncio.run(main())
281+
```
282+
283+
### Server Behavior
284+
285+
- If the server doesn't receive an acknowledgment within a certain timeframe, it will attempt to resend the message.
286+
- The server will make multiple attempts to send a message before considering it failed.
287+
288+
### Enabling the Feature
289+
290+
To enable this feature, set the `INTERPRETER_REQUIRE_ACKNOWLEDGE` environment variable to `"True"` before starting the server:
291+
292+
```bash
293+
export INTERPRETER_REQUIRE_ACKNOWLEDGE="True"
294+
interpreter --server
295+
```
296+
297+
Or in Python:
298+
299+
```python
300+
import os
301+
os.environ["INTERPRETER_REQUIRE_ACKNOWLEDGE"] = "True"
302+
303+
from interpreter import AsyncInterpreter
304+
async_interpreter = AsyncInterpreter()
305+
async_interpreter.server.run()
306+
```
307+
308+
## Advanced Usage: Accessing the FastAPI App Directly
309+
310+
The FastAPI app is exposed at `async_interpreter.server.app`. This allows you to add custom routes or host the app using Uvicorn directly.
311+
312+
Example of adding a custom route and hosting with Uvicorn:
313+
314+
```python
315+
from interpreter import AsyncInterpreter
316+
from fastapi import FastAPI
317+
import uvicorn
318+
319+
async_interpreter = AsyncInterpreter()
320+
app = async_interpreter.server.app
321+
322+
@app.get("/custom")
323+
async def custom_route():
324+
return {"message": "This is a custom route"}
325+
326+
if __name__ == "__main__":
327+
uvicorn.run(app, host="0.0.0.0", port=8000)
328+
```
329+
166330
## Best Practices
331+
167332
1. Always handle the "complete" status message to ensure your client knows when the server has finished processing.
168333
2. If `auto_run` is set to `False`, remember to send the "go" command to execute code blocks and continue the interaction.
169334
3. Implement proper error handling in your client to manage potential connection issues, unexpected server responses, or server-sent error messages.
170335
4. Use the AsyncInterpreter class when working with the server in Python to ensure compatibility with asynchronous operations.
171-
5. When deploying in production, consider using the Docker container for easier setup and consistent environment across different machines.
336+
5. Pay attention to the code execution review messages for important safety and operational information.
337+
6. Utilize the multi-part user message structure for complex inputs, including file paths and images.
338+
7. When sending file paths or image paths, ensure they are accessible to the server.

docs/usage/python/multiple-instances.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def swap_roles(messages):
2424
agents = [agent_1, agent_2]
2525

2626
# Kick off the conversation
27-
messages = [{"role": "user", "message": "Hello!"}]
27+
messages = [{"role": "user", "type": "message", "content": "Hello!"}]
2828

2929
while True:
3030
for agent in agents:

0 commit comments

Comments
 (0)