Skip to content

Commit ccd034a

Browse files
committed
Added templates, improved --local
1 parent ee18283 commit ccd034a

File tree

5 files changed

+110
-56
lines changed

5 files changed

+110
-56
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
llama.log
2+
13
# Byte-compiled / optimized / DLL files
24
__pycache__/
35
*.py[cod]

interpreter/core/core.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,17 @@ def __init__(
6767
llm=None,
6868
system_message=default_system_message,
6969
custom_instructions="",
70+
user_message_template="{content}",
71+
code_output_template="Code output: {content}\n\nWhat does this output mean / what's next (if anything, or are we done)?",
72+
empty_code_output_template="The code above was executed on my machine. It produced no text output. what's next (if anything, or are we done?)",
73+
code_output_sender="user",
7074
computer=None,
7175
sync_computer=False,
7276
import_computer_api=False,
7377
skills_path=None,
7478
import_skills=False,
7579
multi_line=False,
76-
contribute_conversation=False
80+
contribute_conversation=False,
7781
):
7882
# State
7983
self.messages = [] if messages is None else messages
@@ -124,6 +128,10 @@ def __init__(
124128
# These are LLM related
125129
self.system_message = system_message
126130
self.custom_instructions = custom_instructions
131+
self.user_message_template = user_message_template
132+
self.code_output_template = code_output_template
133+
self.empty_code_output_template = empty_code_output_template
134+
self.code_output_sender = code_output_sender
127135

128136
def server(self, *args, **kwargs):
129137
server(self, *args, **kwargs)
@@ -140,7 +148,9 @@ def anonymous_telemetry(self) -> bool:
140148

141149
@property
142150
def will_contribute(self):
143-
overrides = self.offline or not self.conversation_history or self.disable_telemetry
151+
overrides = (
152+
self.offline or not self.conversation_history or self.disable_telemetry
153+
)
144154
return self.contribute_conversation and not overrides
145155

146156
def chat(self, message=None, display=True, stream=False, blocking=True):
@@ -245,7 +255,9 @@ def _streaming_chat(self, message=None, display=True):
245255
# If it's the first message, set the conversation name
246256
if not self.conversation_filename:
247257
first_few_words_list = self.messages[0]["content"][:25].split(" ")
248-
if len(first_few_words_list) >= 2: # for languages like English with blank between words
258+
if (
259+
len(first_few_words_list) >= 2
260+
): # for languages like English with blank between words
249261
first_few_words = "_".join(first_few_words_list[:-1])
250262
else: # for languages like Chinese without blank between words
251263
first_few_words = self.messages[0]["content"][:15]

interpreter/core/llm/llm.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ def __init__(self, interpreter):
2828
self.model = "gpt-4-turbo"
2929
self.temperature = 0
3030

31-
self.supports_vision = None # Will try to auto-detect
32-
self.vision_renderer = self.interpreter.computer.vision.query # Will only use if supports_vision is False
31+
self.supports_vision = None # Will try to auto-detect
32+
self.vision_renderer = (
33+
self.interpreter.computer.vision.query
34+
) # Will only use if supports_vision is False
3335

34-
self.supports_functions = None # Will try to auto-detect
35-
self.execution_instructions = "To execute code on the user's machine, write a markdown code block. Specify the language after the ```. You will receive the output. Use any programming language." # If supports_functions is False, this will be added to the system message
36+
self.supports_functions = None # Will try to auto-detect
37+
self.execution_instructions = "To execute code on the user's machine, write a markdown code block. Specify the language after the ```. You will receive the output. Use any programming language." # If supports_functions is False, this will be added to the system message
3638

3739
# Optional settings
3840
self.context_window = None
@@ -81,7 +83,7 @@ def run(self, messages):
8183
self.supports_vision = False
8284
except:
8385
self.supports_vision = False
84-
86+
8587
# Trim image messages if they're there
8688
image_messages = [msg for msg in messages if msg["type"] == "image"]
8789
if self.supports_vision:
@@ -103,15 +105,19 @@ def run(self, messages):
103105
elif self.supports_vision == False and self.vision_renderer:
104106
for img_msg in image_messages:
105107
if img_msg["format"] != "description":
106-
img_msg["content"] = "Imagine I have just shown you an image with this description: " + self.vision_renderer(lmc=img_msg)
107-
img_msg["format"] = "description"
108+
img_msg["content"] = (
109+
"Imagine I have just shown you an image with this description: "
110+
+ self.vision_renderer(lmc=img_msg)
111+
)
112+
img_msg["format"] = "description"
108113

109114
# Convert to OpenAI messages format
110115
messages = convert_to_openai_messages(
111116
messages,
112117
function_calling=self.supports_functions,
113118
vision=self.supports_vision,
114119
shrink_images=self.interpreter.shrink_images,
120+
interpreter=self.interpreter,
115121
)
116122

117123
system_message = messages[0]["content"]

interpreter/core/llm/utils/convert_to_openai_messages.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def convert_to_openai_messages(
1010
function_calling=True,
1111
vision=False,
1212
shrink_images=True,
13-
code_output_sender="assistant",
13+
interpreter=None,
1414
):
1515
"""
1616
Converts LMC messages into OpenAI messages
@@ -27,7 +27,7 @@ def convert_to_openai_messages(
2727
# message["type"] = "message"
2828
# message["content"] = "```" + message.get("format", "") + "\n" + message.get("content").strip("\n`") + "\n```"
2929
# prev_message = message
30-
30+
3131
# messages = [message for message in messages if message.get("type") != "code"]
3232

3333
for message in messages:
@@ -41,7 +41,15 @@ def convert_to_openai_messages(
4141
new_message["role"] = message[
4242
"role"
4343
] # This should never be `computer`, right?
44-
new_message["content"] = message["content"]
44+
45+
if (
46+
message["role"] == "user" and message == messages[-1]
47+
): # Only add the template for the last message?
48+
new_message["content"] = interpreter.user_message_template.replace(
49+
"{content}", message["content"]
50+
)
51+
else:
52+
new_message["content"] = message["content"]
4553

4654
elif message["type"] == "code":
4755
new_message["role"] = "assistant"
@@ -79,19 +87,17 @@ def convert_to_openai_messages(
7987

8088
else:
8189
# This should be experimented with.
82-
if code_output_sender == "user":
90+
if interpreter.code_output_sender == "user":
8391
if message["content"].strip() == "":
84-
content = "The code above was executed on my machine. It produced no text output. what's next (if anything, or are we done?)"
92+
content = interpreter.empty_code_output_template
8593
else:
86-
content = (
87-
"Code output: "
88-
+ message["content"]
89-
+ "\n\nWhat does this output mean / what's next (if anything, or are we done)?"
94+
content = interpreter.code_output_template.replace(
95+
"{content}", message["content"]
9096
)
9197

9298
new_message["role"] = "user"
9399
new_message["content"] = content
94-
elif code_output_sender == "assistant":
100+
elif interpreter.code_output_sender == "assistant":
95101
if "@@@SEND_MESSAGE_AS_USER@@@" in message["content"]:
96102
new_message["role"] = "user"
97103
new_message["content"] = message["content"].replace(
@@ -136,7 +142,9 @@ def convert_to_openai_messages(
136142
# Convert the image back to base64
137143
buffered = io.BytesIO()
138144
img.save(buffered, format=extension)
139-
img_str = base64.b64encode(buffered.getvalue()).decode("utf-8")
145+
img_str = base64.b64encode(buffered.getvalue()).decode(
146+
"utf-8"
147+
)
140148
content = f"data:image/{extension};base64,{img_str}"
141149
except:
142150
# This should be non blocking. It's not required
@@ -149,7 +157,9 @@ def convert_to_openai_messages(
149157
file_extension = image_path.split(".")[-1]
150158

151159
with open(image_path, "rb") as image_file:
152-
encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
160+
encoded_string = base64.b64encode(image_file.read()).decode(
161+
"utf-8"
162+
)
153163

154164
content = f"data:image/{file_extension};base64,{encoded_string}"
155165
else:
@@ -158,7 +168,9 @@ def convert_to_openai_messages(
158168
if "format" not in message:
159169
raise Exception("Format of the image is not specified.")
160170
else:
161-
raise Exception(f"Unrecognized image format: {message['format']}")
171+
raise Exception(
172+
f"Unrecognized image format: {message['format']}"
173+
)
162174

163175
# Calculate the size of the original binary data in bytes
164176
content_size_bytes = len(content) * 3 / 4
@@ -206,27 +218,24 @@ def convert_to_openai_messages(
206218
elif current_role == message["role"]:
207219
current_content.append(message["content"])
208220
else:
209-
combined_messages.append({
210-
"role": current_role,
211-
"content": "\n".join(current_content)
212-
})
221+
combined_messages.append(
222+
{"role": current_role, "content": "\n".join(current_content)}
223+
)
213224
current_role = message["role"]
214225
current_content = [message["content"]]
215226
else:
216227
if current_content:
217-
combined_messages.append({
218-
"role": current_role,
219-
"content": "\n".join(current_content)
220-
})
228+
combined_messages.append(
229+
{"role": current_role, "content": "\n".join(current_content)}
230+
)
221231
current_content = []
222232
combined_messages.append(message)
223233

224234
# Add the last message
225235
if current_content:
226-
combined_messages.append({
227-
"role": current_role,
228-
"content": " ".join(current_content)
229-
})
236+
combined_messages.append(
237+
{"role": current_role, "content": " ".join(current_content)}
238+
)
230239

231240
new_messages = combined_messages
232241

interpreter/terminal_interface/profiles/defaults/local.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@
33
import subprocess
44
import sys
55
import time
6+
67
import inquirer
78
import psutil
89
import wget
10+
911
from interpreter import interpreter
1012

11-
def get_ram():
12-
total_ram = psutil.virtual_memory().total / (
13-
1024 * 1024 * 1024
14-
) # Convert bytes to GB
15-
return total_ram
1613

1714
def download_model(models_dir, models, interpreter):
1815
# Get RAM and disk information
19-
total_ram = get_ram()
16+
total_ram = psutil.virtual_memory().total / (
17+
1024 * 1024 * 1024
18+
) # Convert bytes to GB
2019
free_disk_space = psutil.disk_usage("/").free / (
2120
1024 * 1024 * 1024
2221
) # Convert bytes to GB
@@ -53,6 +52,18 @@ def download_model(models_dir, models, interpreter):
5352

5453
try:
5554
model_list = [
55+
{
56+
"name": "Llama-3-8B-Instruct",
57+
"file_name": " Meta-Llama-3-8B-Instruct.Q5_K_M.llamafile",
58+
"size": 5.76,
59+
"url": "https://huggingface.co/jartine/Meta-Llama-3-8B-Instruct-llamafile/resolve/main/Meta-Llama-3-8B-Instruct.Q5_K_M.llamafile?download=true",
60+
},
61+
{
62+
"name": "Phi-3-mini",
63+
"file_name": "Phi-3-mini-4k-instruct.Q5_K_M.llamafile",
64+
"size": 2.84,
65+
"url": "https://huggingface.co/jartine/Phi-3-mini-4k-instruct-llamafile/resolve/main/Phi-3-mini-4k-instruct.Q5_K_M.llamafile?download=true",
66+
},
5667
{
5768
"name": "TinyLlama-1.1B",
5869
"file_name": "TinyLlama-1.1B-Chat-v1.0.Q5_K_M.llamafile",
@@ -71,12 +82,6 @@ def download_model(models_dir, models, interpreter):
7182
"size": 1.96,
7283
"url": "https://huggingface.co/jartine/phi-2-llamafile/resolve/main/phi-2.Q5_K_M.llamafile?download=true",
7384
},
74-
{
75-
"name": "Phi-3-mini",
76-
"file_name": "Phi-3-mini-4k-instruct.Q5_K_M.llamafile",
77-
"size": 2.84,
78-
"url": "https://huggingface.co/jartine/Phi-3-mini-4k-instruct-llamafile/resolve/main/Phi-3-mini-4k-instruct.Q5_K_M.llamafile?download=true",
79-
},
8085
{
8186
"name": "LLaVA 1.5",
8287
"file_name": "llava-v1.5-7b-q4.llamafile",
@@ -89,12 +94,6 @@ def download_model(models_dir, models, interpreter):
8994
"size": 5.15,
9095
"url": "https://huggingface.co/jartine/Mistral-7B-Instruct-v0.2-llamafile/resolve/main/mistral-7b-instruct-v0.2.Q5_K_M.llamafile?download=true",
9196
},
92-
{
93-
"name": "Llama-3-8B-Instruct",
94-
"file_name": " Meta-Llama-3-8B-Instruct.Q5_K_M.llamafile",
95-
"size": 5.76,
96-
"url": "https://huggingface.co/jartine/Meta-Llama-3-8B-Instruct-llamafile/resolve/main/Meta-Llama-3-8B-Instruct.Q5_K_M.llamafile?download=true",
97-
},
9897
{
9998
"name": "WizardCoder-Python-13B",
10099
"file_name": "wizardcoder-python-13b.llamafile",
@@ -136,7 +135,7 @@ def download_model(models_dir, models, interpreter):
136135
)
137136
]
138137
answers = inquirer.prompt(questions)
139-
138+
140139
if answers == None:
141140
exit()
142141

@@ -373,7 +372,7 @@ def list_ollama_models():
373372
)
374373
]
375374
answers = inquirer.prompt(questions)
376-
375+
377376
if answers == None:
378377
exit()
379378

@@ -410,7 +409,9 @@ def list_ollama_models():
410409
interpreter.llm.api_base = "http://localhost:8080/v1"
411410
interpreter.llm.supports_functions = False
412411

413-
user_ram = get_ram()
412+
user_ram = total_ram = psutil.virtual_memory().total / (
413+
1024 * 1024 * 1024
414+
) # Convert bytes to GB
414415
# Set context window and max tokens for all local models based on the users available RAM
415416
if user_ram and user_ram > 9:
416417
interpreter.llm.max_tokens = 1200
@@ -434,5 +435,29 @@ def list_ollama_models():
434435
Once you have accomplished the task, ask the user if they are happy with the result and wait for their response. It is very important to get feedback from the user.
435436
The user will tell you the next task after you ask them.
436437
"""
438+
439+
interpreter.system_message = """You are an AI assistant that writes markdown code snippets to answer the user's request. You speak very concisely and quickly, you say nothing irrelevant to the user's request. For example:
440+
441+
User: Open the chrome app.
442+
Assistant: On it.
443+
```python
444+
import webbrowser
445+
webbrowser.open('https://chrome.google.com')
446+
```
447+
User: The code you ran produced no output. Was this expected, or are we finished?
448+
Assistant: No further action is required; the provided snippet opens Chrome.
449+
450+
Now, your turn:
451+
"""
452+
453+
interpreter.user_message_template = "{content} Please send me some code that would be able to answer my question, in the form of ```python\n... the code ...\n``` or ```shell\n... the code ...\n```"
454+
interpreter.code_output_template = "I executed that code. This was the ouput: {content}\n\nWhat does this output mean / what's next (if anything, or are we done)?"
455+
interpreter.empty_code_output_template = "The code above was executed on my machine. It produced no text output. what's next (if anything, or are we done?)"
456+
interpreter.code_output_sender = "user"
457+
interpreter.max_output = 500
458+
interpreter.llm.context_window = 8000
459+
interpreter.force_task_completion = False
460+
# interpreter.user_message_template = "{content}. If my question must be solved by running code on my computer, send me code to run enclosed in ```python or ```shell. Otherwise, don't send code and answer like a chatbot. Be concise, don't include anything unnecessary. Don't use placeholders, I can't edit code."
461+
437462
# Set offline for all local models
438463
interpreter.offline = True

0 commit comments

Comments
 (0)