Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions docs/server/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,79 @@ docker run -p 8000:8000 open-interpreter

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

## Acknowledgment Feature

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.

### How it works

1. When this feature is enabled, each message sent by the server will include an `id` field.
2. The client must send an acknowledgment message back to the server for each received message.
3. The server will wait for this acknowledgment before sending the next message.

### Client Implementation

To implement this on the client side:

1. Check if each received message contains an `id` field.
2. If an `id` is present, send an acknowledgment message back to the server.

Here's an example of how to handle this in your WebSocket client:

```python
import json
import websockets

async def handle_messages(websocket):
async for message in websocket:
data = json.loads(message)

# Process the message as usual
print(f"Received: {data}")

# Check if the message has an ID that needs to be acknowledged
if "id" in data:
ack_message = {
"ack": data["id"]
}
await websocket.send(json.dumps(ack_message))
print(f"Sent acknowledgment for message {data['id']}")

async def main():
uri = "ws://localhost:8000"
async with websockets.connect(uri) as websocket:
await handle_messages(websocket)

# Run the async function
import asyncio
asyncio.run(main())
```

### Server Behavior

- If the server doesn't receive an acknowledgment within a certain timeframe, it will attempt to resend the message.
- The server will make multiple attempts to send a message before considering it failed.

### Enabling the Feature

To enable this feature, set the `INTERPRETER_REQUIRE_ACKNOWLEDGE` environment variable to `"True"` before starting the server:

```bash
export INTERPRETER_REQUIRE_ACKNOWLEDGE="True"
interpreter --server
```

Or in Python:

```python
import os
os.environ["INTERPRETER_REQUIRE_ACKNOWLEDGE"] = "True"

from interpreter import AsyncInterpreter
async_interpreter = AsyncInterpreter()
async_interpreter.server.run()
```

## Advanced Usage: Accessing the FastAPI App Directly

The FastAPI app is exposed at `async_interpreter.server.app`. This allows you to add custom routes or host the app using Uvicorn directly.
Expand Down
55 changes: 34 additions & 21 deletions interpreter/core/async_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,6 @@ def accumulate(self, chunk):
chunk_copy["content"] = ""
self.messages.append(chunk_copy)

print("ADDED CHUNK:", chunk)
print("MESSAGES IS NOW:", self.messages)
# time.sleep(5)

elif type(chunk) == bytes:
if self.messages[-1]["content"] == "": # We initialize as an empty string ^
self.messages[-1]["content"] = b"" # But it actually should be bytes
Expand Down Expand Up @@ -282,7 +278,7 @@ async def home():
} else {
var startMessageBlock = {
"role": "user",
"type": "message",
//"type": "message",
"start": true
};
ws.send(JSON.stringify(startMessageBlock));
Expand All @@ -296,7 +292,7 @@ async def home():

var endMessageBlock = {
"role": "user",
"type": "message",
//"type": "message",
"end": true
};
ws.send(JSON.stringify(endMessageBlock));
Expand Down Expand Up @@ -649,21 +645,38 @@ async def chat_completion(request: ChatCompletionRequest):


class Server:
def __init__(self, async_interpreter, host=host, port=port):
def __init__(self, async_interpreter, host="127.0.0.1", port=8000):
self.app = FastAPI()
router = create_router(async_interpreter)
self.app.include_router(router)
self.host = host
self.port = port

def run(self, retries=5, *args, **kwargs):
if "host" in kwargs:
self.host = kwargs.pop("host")
if "port" in kwargs:
self.port = kwargs.pop("port")
if "app" in kwargs:
self.app = kwargs.pop("app")

self.config = uvicorn.Config(app=self.app, host=host, port=port)
self.uvicorn_server = uvicorn.Server(self.config)

@property
def host(self):
return self.config.host

@host.setter
def host(self, value):
self.config.host = value
self.uvicorn_server = uvicorn.Server(self.config)

@property
def port(self):
return self.config.port

@port.setter
def port(self, value):
self.config.port = value
self.uvicorn_server = uvicorn.Server(self.config)

def run(self, host=None, port=None, retries=5):
if host is not None:
self.host = host
if port is not None:
self.port = port

# Print server information
if self.host == "0.0.0.0":
print(
"Warning: Using host `0.0.0.0` will expose Open Interpreter over your local network."
Expand All @@ -672,12 +685,12 @@ def run(self, retries=5, *args, **kwargs):
s.connect(("8.8.8.8", 80)) # Google's public DNS server
print(f"Server will run at http://{s.getsockname()[0]}:{self.port}")
s.close()
else:
print(f"Server will run at http://{self.host}:{self.port}")

for _ in range(retries):
try:
uvicorn.run(
app=self.app, host=self.host, port=self.port, *args, **kwargs
)
self.uvicorn_server.run()
break
except KeyboardInterrupt:
break
Expand Down
Loading