Skip to content

Commit 2ae7bd3

Browse files
committed
Added server image test, server stop, added server acknowledgement docs
1 parent 24e07c9 commit 2ae7bd3

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

docs/server/usage.mdx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,79 @@ docker run -p 8000:8000 open-interpreter
232232

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

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+
235308
## Advanced Usage: Accessing the FastAPI App Directly
236309

237310
The FastAPI app is exposed at `async_interpreter.server.app`. This allows you to add custom routes or host the app using Uvicorn directly.

tests/test_interpreter.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,83 @@ async def test_fastapi_server():
341341
)
342342
assert response.strip(" \n.").lower() == "no"
343343

344+
#### TEST IMAGES ####
345+
346+
# Send another POST request
347+
post_url = "http://localhost:8000/settings"
348+
settings = {"messages": [], "auto_run": True}
349+
response = requests.post(post_url, json=settings)
350+
print("POST request sent, response:", response.json())
351+
352+
base64png = "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC"
353+
354+
# Sending messages via WebSocket
355+
await websocket.send(json.dumps({"role": "user", "start": True}))
356+
await websocket.send(
357+
json.dumps(
358+
{
359+
"role": "user",
360+
"type": "message",
361+
"content": "describe these images",
362+
}
363+
)
364+
)
365+
await websocket.send(
366+
json.dumps(
367+
{
368+
"role": "user",
369+
"type": "image",
370+
"format": "base64.png",
371+
"content": base64png,
372+
}
373+
)
374+
)
375+
# await websocket.send(
376+
# json.dumps(
377+
# {
378+
# "role": "user",
379+
# "type": "image",
380+
# "format": "path",
381+
# "content": "/Users/killianlucas/Documents/GitHub/open-interpreter/screen.png",
382+
# }
383+
# )
384+
# )
385+
386+
await websocket.send(json.dumps({"role": "user", "end": True}))
387+
print("WebSocket chunks sent")
388+
389+
# Wait for response
390+
accumulated_content = ""
391+
while True:
392+
message = await websocket.recv()
393+
message_data = json.loads(message)
394+
if "error" in message_data:
395+
raise Exception(message_data["content"])
396+
print("Received from WebSocket:", message_data)
397+
if type(message_data.get("content")) == str:
398+
accumulated_content += message_data.get("content")
399+
if message_data == {
400+
"role": "server",
401+
"type": "status",
402+
"content": "complete",
403+
}:
404+
print("Received expected message from server")
405+
break
406+
407+
# Get messages
408+
get_url = "http://localhost:8000/settings/messages"
409+
response_json = requests.get(get_url).json()
410+
print("GET request sent, response:", response_json)
411+
if isinstance(response_json, str):
412+
response_json = json.loads(response_json)
413+
messages = response_json["messages"]
414+
415+
response = async_interpreter.computer.ai.chat(
416+
str(messages)
417+
+ "\n\nIn the conversation above, does the assistant appear to be able to describe the images? Yes or no? Only reply with one word— 'yes' or 'no'."
418+
)
419+
assert response.strip(" \n.").lower() == "yes"
420+
344421
# Sending POST request to /run endpoint with code to kill a thread in Python
345422
# actually wait i dont think this will work..? will just kill the python interpreter
346423
post_url = "http://localhost:8000/run"

0 commit comments

Comments
 (0)