Skip to content

Commit 20bc10f

Browse files
committed
Working server for non-Anthropic LLMs
1 parent 84a7512 commit 20bc10f

File tree

4 files changed

+82
-68
lines changed

4 files changed

+82
-68
lines changed

interpreter_1/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ def load_interpreter():
178178
if args["serve"]:
179179
# Load interpreter immediately for server mode
180180
load_interpreter()
181+
print("Starting server...")
181182
interpreter.server()
182183
return
183184

interpreter_1/interpreter.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ async def async_respond(self):
253253
first_token = True
254254

255255
for chunk in raw_response:
256+
yield chunk
257+
256258
if first_token:
257259
self._spinner.stop()
258260
first_token = False
@@ -423,11 +425,9 @@ async def async_respond(self):
423425
self.messages.append(
424426
{"content": tool_result_content, "role": "user"}
425427
)
426-
yield {"type": "messages", "messages": self.messages}
427428
break
428429

429430
if not tool_result_content:
430-
yield {"type": "messages", "messages": self.messages}
431431
break
432432

433433
self.messages.append(
@@ -500,6 +500,8 @@ async def async_respond(self):
500500
first_token = True
501501

502502
for chunk in raw_response:
503+
yield chunk
504+
503505
if first_token:
504506
self._spinner.stop()
505507
first_token = False
@@ -508,11 +510,12 @@ async def async_respond(self):
508510
message = chunk.choices[0].delta
509511

510512
if chunk.choices[0].delta.content:
511-
yield {"type": "chunk", "chunk": chunk.choices[0].delta.content}
512513
md.feed(chunk.choices[0].delta.content)
513514
await asyncio.sleep(0)
514515

515-
if chunk.choices[0].delta != message:
516+
if message.content == None:
517+
message.content = chunk.choices[0].delta.content
518+
elif chunk.choices[0].delta.content != None:
516519
message.content += chunk.choices[0].delta.content
517520

518521
if chunk.choices[0].delta.tool_calls:
@@ -561,10 +564,12 @@ async def async_respond(self):
561564
print()
562565

563566
if not message.tool_calls:
564-
yield {"type": "messages", "messages": self.messages}
565567
break
566568

567-
user_approval = input("\nRun tool(s)? (y/n): ").lower().strip()
569+
if self.auto_run:
570+
user_approval = "y"
571+
else:
572+
user_approval = input("\nRun tool(s)? (y/n): ").lower().strip()
568573

569574
for tool_call in message.tool_calls:
570575
function_arguments = json.loads(tool_call.function.arguments)
@@ -887,13 +892,6 @@ def server(self):
887892
"""
888893
from .server import Server
889894

890-
# Initialize messages if not already set
891-
if not hasattr(self, "messages"):
892-
self.messages = []
893-
894-
# Set auto_run to True for server mode
895-
self.auto_run = True
896-
897895
# Create and start server
898896
server = Server(self)
899897
try:

interpreter_1/server.py

Lines changed: 69 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,24 @@
22
import json
33
import os
44
import time
5-
from typing import Any, Dict, List, Optional
5+
from typing import Any, Dict, List, Optional, Union
66

77
import uvicorn
88
from fastapi import FastAPI, Request
99
from fastapi.responses import JSONResponse, StreamingResponse
10+
from pydantic import BaseModel
1011

1112

12-
class ChatCompletionRequest:
13-
def __init__(
14-
self,
15-
messages: List[Dict[str, str]],
16-
stream: bool = False,
17-
model: Optional[str] = None,
18-
):
19-
self.messages = messages
20-
self.stream = stream
21-
self.model = model
13+
class ChatCompletionRequest(BaseModel):
14+
messages: List[Dict[str, Union[str, list, None]]]
15+
stream: bool = False
16+
model: Optional[str] = None
17+
temperature: Optional[float] = None
18+
max_tokens: Optional[int] = None
19+
top_p: Optional[float] = None
20+
frequency_penalty: Optional[float] = None
21+
presence_penalty: Optional[float] = None
22+
tools: Optional[List[Dict[str, Any]]] = None
2223

2324

2425
class Server:
@@ -33,30 +34,22 @@ def __init__(self, interpreter):
3334

3435
# Setup routes
3536
self.app.post("/v1/chat/completions")(self.chat_completion)
36-
self.app.get("/v1/models")(self.list_models)
37-
38-
async def list_models(self):
39-
"""List available models endpoint"""
40-
return {
41-
"data": [
42-
{
43-
"id": self.interpreter.model,
44-
"object": "model",
45-
"created": int(time.time()),
46-
"owned_by": "open-interpreter",
47-
}
48-
]
49-
}
5037

5138
async def chat_completion(self, request: Request):
5239
"""Main chat completion endpoint"""
5340
body = await request.json()
54-
req = ChatCompletionRequest(**body)
41+
try:
42+
req = ChatCompletionRequest(**body)
43+
except Exception as e:
44+
print("Validation error:", str(e)) # Debug print
45+
print("Request body:", body) # Print the request body
46+
raise
47+
48+
# Filter out system message
49+
req.messages = [msg for msg in req.messages if msg["role"] != "system"]
5550

5651
# Update interpreter messages
57-
self.interpreter.messages = [
58-
{"role": msg["role"], "content": msg["content"]} for msg in req.messages
59-
]
52+
self.interpreter.messages = req.messages
6053

6154
if req.stream:
6255
return StreamingResponse(
@@ -85,33 +78,54 @@ async def chat_completion(self, request: Request):
8578

8679
async def _stream_response(self):
8780
"""Stream the response in OpenAI-compatible format"""
88-
for chunk in self.interpreter.respond():
89-
if chunk.get("type") == "chunk":
90-
data = {
91-
"id": "chatcmpl-" + str(time.time()),
92-
"object": "chat.completion.chunk",
93-
"created": int(time.time()),
94-
"model": self.interpreter.model,
95-
"choices": [
96-
{
97-
"index": 0,
98-
"delta": {"content": chunk["chunk"]},
99-
"finish_reason": None,
100-
}
101-
],
102-
}
103-
yield f"data: {json.dumps(data)}\n\n"
104-
await asyncio.sleep(0)
81+
async for chunk in self.interpreter.async_respond():
82+
# Convert tool_calls to dict if present
83+
choices = []
84+
for choice in chunk.choices:
85+
delta = {}
86+
if choice.delta:
87+
if choice.delta.content is not None:
88+
delta["content"] = choice.delta.content
89+
if choice.delta.role is not None:
90+
delta["role"] = choice.delta.role
91+
if choice.delta.function_call is not None:
92+
delta["function_call"] = choice.delta.function_call
93+
if choice.delta.tool_calls is not None:
94+
pass
95+
# Convert tool_calls to dict representation
96+
# delta["tool_calls"] = [
97+
# {
98+
# "index": tool_call.index,
99+
# "id": tool_call.id,
100+
# "type": tool_call.type,
101+
# "function": {
102+
# "name": tool_call.function.name,
103+
# "arguments": tool_call.function.arguments
104+
# }
105+
# } for tool_call in choice.delta.tool_calls
106+
# ]
107+
108+
choices.append(
109+
{
110+
"index": choice.index,
111+
"delta": delta,
112+
"finish_reason": choice.finish_reason,
113+
}
114+
)
115+
116+
data = {
117+
"id": chunk.id,
118+
"object": chunk.object,
119+
"created": chunk.created,
120+
"model": chunk.model,
121+
"choices": choices,
122+
}
123+
124+
if hasattr(chunk, "system_fingerprint"):
125+
data["system_fingerprint"] = chunk.system_fingerprint
126+
127+
yield f"data: {json.dumps(data)}\n\n"
105128

106-
# Send final chunk
107-
data = {
108-
"id": "chatcmpl-" + str(time.time()),
109-
"object": "chat.completion.chunk",
110-
"created": int(time.time()),
111-
"model": self.interpreter.model,
112-
"choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}],
113-
}
114-
yield f"data: {json.dumps(data)}\n\n"
115129
yield "data: [DONE]\n\n"
116130

117131
def run(self):

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pyte = "^0.8.2"
2424
screeninfo = "^0.8.1"
2525
readchar = "^4.2.1"
2626
pillow = "^11.0.0"
27+
uvicorn = "^0.32.0"
2728

2829
[build-system]
2930
requires = ["poetry-core>=1.0.0"]

0 commit comments

Comments
 (0)