|
""" |
|
The terminal interface is just a view. Just handles the very top layer. |
|
If you were to build a frontend this would be a way to do it. |
|
""" |
|
|
|
try: |
|
import readline |
|
except ImportError: |
|
pass |
|
|
|
import os |
|
import platform |
|
import random |
|
import re |
|
import subprocess |
|
import time |
|
|
|
from ..core.utils.scan_code import scan_code |
|
from ..core.utils.system_debug_info import system_info |
|
from ..core.utils.truncate_output import truncate_output |
|
from .components.code_block import CodeBlock |
|
from .components.message_block import MessageBlock |
|
from .magic_commands import handle_magic_command |
|
from .utils.check_for_package import check_for_package |
|
from .utils.display_markdown_message import display_markdown_message |
|
from .utils.display_output import display_output |
|
from .utils.find_image_path import find_image_path |
|
from .utils.cli_input import cli_input |
|
|
|
|
|
examples = [ |
|
"How many files are on my desktop?", |
|
"What time is it in Seattle?", |
|
"Make me a simple Pomodoro app.", |
|
"Open Chrome and go to YouTube.", |
|
"Can you set my system to light mode?", |
|
] |
|
random.shuffle(examples) |
|
try: |
|
for example in examples: |
|
readline.add_history(example) |
|
except: |
|
|
|
pass |
|
|
|
|
|
def terminal_interface(interpreter, message): |
|
|
|
|
|
if not interpreter.auto_run and not interpreter.offline: |
|
interpreter_intro_message = [ |
|
"**Open Interpreter** will require approval before running code." |
|
] |
|
|
|
if interpreter.safe_mode == "ask" or interpreter.safe_mode == "auto": |
|
if not check_for_package("semgrep"): |
|
interpreter_intro_message.append( |
|
f"**Safe Mode**: {interpreter.safe_mode}\n\n>Note: **Safe Mode** requires `semgrep` (`pip install semgrep`)" |
|
) |
|
else: |
|
interpreter_intro_message.append("Use `interpreter -y` to bypass this.") |
|
|
|
interpreter_intro_message.append("Press `CTRL-C` to exit.") |
|
|
|
display_markdown_message("\n\n".join(interpreter_intro_message) + "\n") |
|
|
|
if message: |
|
interactive = False |
|
else: |
|
interactive = True |
|
|
|
active_block = None |
|
voice_subprocess = None |
|
|
|
while True: |
|
if interactive: |
|
|
|
message = cli_input("> ").strip() if interpreter.multi_line else input("> ").strip() |
|
|
|
try: |
|
|
|
readline.add_history(message) |
|
except: |
|
|
|
pass |
|
|
|
if isinstance(message, str): |
|
|
|
|
|
|
|
if message == "": |
|
|
|
continue |
|
|
|
if message.startswith("%") and interactive: |
|
handle_magic_command(interpreter, message) |
|
continue |
|
|
|
|
|
if message.strip() == "interpreter --local": |
|
print("Please exit this conversation, then run `interpreter --local`.") |
|
continue |
|
if message.strip() == "pip install --upgrade open-interpreter": |
|
print( |
|
"Please exit this conversation, then run `pip install --upgrade open-interpreter`." |
|
) |
|
continue |
|
|
|
if interpreter.llm.supports_vision or interpreter.llm.vision_renderer != None: |
|
|
|
image_path = find_image_path(message) |
|
|
|
|
|
if image_path: |
|
|
|
interpreter.messages.append( |
|
{ |
|
"role": "user", |
|
"type": "message", |
|
"content": message, |
|
} |
|
) |
|
|
|
|
|
message = { |
|
"role": "user", |
|
"type": "image", |
|
"format": "path", |
|
"content": image_path, |
|
} |
|
|
|
try: |
|
for chunk in interpreter.chat(message, display=False, stream=True): |
|
yield chunk |
|
|
|
|
|
if "recipient" in chunk and chunk["recipient"] != "user": |
|
continue |
|
|
|
if interpreter.verbose: |
|
print("Chunk in `terminal_interface`:", chunk) |
|
|
|
|
|
|
|
if interpreter.os: |
|
if ( |
|
chunk.get("format") == "output" |
|
and "failsafeexception" in chunk["content"].lower() |
|
): |
|
print("Fail-safe triggered (mouse in one of the four corners).") |
|
break |
|
|
|
if "end" in chunk and active_block: |
|
active_block.refresh(cursor=False) |
|
|
|
if chunk["type"] in [ |
|
"message", |
|
"console", |
|
]: |
|
active_block.end() |
|
active_block = None |
|
|
|
|
|
if chunk["type"] == "message": |
|
if "start" in chunk: |
|
active_block = MessageBlock() |
|
render_cursor = True |
|
|
|
if "content" in chunk: |
|
active_block.message += chunk["content"] |
|
|
|
if "end" in chunk and interpreter.os: |
|
last_message = interpreter.messages[-1]["content"] |
|
|
|
|
|
lines = last_message.split("\n") |
|
i = 0 |
|
while i < len(lines): |
|
|
|
if re.match(r"^\s*([-*]|\d+\.)\s", lines[i]): |
|
del lines[i] |
|
if i > 0: |
|
del lines[i - 1] |
|
i -= 1 |
|
else: |
|
i += 1 |
|
message = "\n".join(lines) |
|
|
|
sanitized_message = ( |
|
message.replace("\\", "\\\\") |
|
.replace("\n", " ") |
|
.replace('"', '\\"') |
|
) |
|
|
|
|
|
if interpreter.os: |
|
interpreter.computer.os.notify(sanitized_message) |
|
|
|
|
|
if platform.system() == "Darwin" and interpreter.speak_messages: |
|
if voice_subprocess: |
|
voice_subprocess.terminate() |
|
voice_subprocess = subprocess.Popen( |
|
[ |
|
"osascript", |
|
"-e", |
|
f'say "{sanitized_message}" using "Fred"', |
|
] |
|
) |
|
else: |
|
pass |
|
|
|
|
|
|
|
|
|
elif chunk["role"] == "assistant" and chunk["type"] == "code": |
|
if "start" in chunk: |
|
active_block = CodeBlock() |
|
active_block.language = chunk["format"] |
|
render_cursor = True |
|
|
|
if "content" in chunk: |
|
active_block.code += chunk["content"] |
|
|
|
|
|
if chunk["type"] == "confirmation": |
|
if not interpreter.auto_run: |
|
|
|
|
|
|
|
if active_block: |
|
active_block.refresh(cursor=False) |
|
active_block.end() |
|
active_block = None |
|
|
|
code_to_run = chunk["content"] |
|
language = code_to_run["format"] |
|
code = code_to_run["content"] |
|
|
|
should_scan_code = False |
|
|
|
if not interpreter.safe_mode == "off": |
|
if interpreter.safe_mode == "auto": |
|
should_scan_code = True |
|
elif interpreter.safe_mode == "ask": |
|
response = input( |
|
" Would you like to scan this code? (y/n)\n\n " |
|
) |
|
print("") |
|
|
|
if response.strip().lower() == "y": |
|
should_scan_code = True |
|
|
|
if should_scan_code: |
|
scan_code(code, language, interpreter) |
|
|
|
response = input( |
|
" Would you like to run this code? (y/n)\n\n " |
|
) |
|
print("") |
|
|
|
if response.strip().lower() == "y": |
|
|
|
|
|
active_block = CodeBlock() |
|
active_block.margin_top = False |
|
active_block.language = language |
|
active_block.code = code |
|
else: |
|
|
|
interpreter.messages.append( |
|
{ |
|
"role": "user", |
|
"type": "message", |
|
"content": "I have declined to run this code.", |
|
} |
|
) |
|
break |
|
|
|
|
|
|
|
if ( |
|
chunk["role"] == "computer" |
|
and "content" in chunk |
|
and ( |
|
chunk["type"] == "image" |
|
or ("format" in chunk and chunk["format"] == "html") |
|
or ("format" in chunk and chunk["format"] == "javascript") |
|
) |
|
): |
|
if interpreter.os and interpreter.verbose == False: |
|
|
|
|
|
continue |
|
|
|
|
|
extra_computer_output = display_output(chunk) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( |
|
interpreter.messages[-1].get("format") != "output" |
|
or interpreter.messages[-1]["role"] != "computer" |
|
or interpreter.messages[-1]["type"] != "console" |
|
): |
|
|
|
interpreter.messages.append( |
|
{ |
|
"role": "computer", |
|
"type": "console", |
|
"format": "output", |
|
"content": extra_computer_output, |
|
} |
|
) |
|
else: |
|
|
|
interpreter.messages[-1]["content"] += ( |
|
"\n" + extra_computer_output |
|
) |
|
interpreter.messages[-1]["content"] = interpreter.messages[-1][ |
|
"content" |
|
].strip() |
|
|
|
|
|
if chunk["type"] == "console": |
|
render_cursor = False |
|
if "format" in chunk and chunk["format"] == "output": |
|
active_block.output += "\n" + chunk["content"] |
|
active_block.output = ( |
|
active_block.output.strip() |
|
) |
|
|
|
|
|
active_block.output = truncate_output( |
|
active_block.output, interpreter.max_output |
|
) |
|
if "format" in chunk and chunk["format"] == "active_line": |
|
active_block.active_line = chunk["content"] |
|
|
|
|
|
if interpreter.os and active_block.active_line != None: |
|
action = "" |
|
|
|
code_lines = active_block.code.split("\n") |
|
if active_block.active_line < len(code_lines): |
|
action = code_lines[active_block.active_line].strip() |
|
|
|
if action.startswith("computer"): |
|
description = None |
|
|
|
|
|
start_index = action.find("(") |
|
end_index = action.rfind(")") |
|
if start_index != -1 and end_index != -1: |
|
|
|
arguments = action[start_index + 1 : end_index] |
|
else: |
|
arguments = None |
|
|
|
|
|
|
|
|
|
|
|
if any(action.startswith(text) for text in [ |
|
"computer.screenshot", |
|
"computer.display.screenshot", |
|
"computer.display.view", |
|
"computer.view" |
|
]): |
|
description = "Viewing screen..." |
|
elif action == "computer.mouse.click()": |
|
description = "Clicking..." |
|
elif action.startswith("computer.mouse.click("): |
|
if "icon=" in arguments: |
|
text_or_icon = "icon" |
|
else: |
|
text_or_icon = "text" |
|
description = f"Clicking {text_or_icon}..." |
|
elif action.startswith("computer.mouse.move("): |
|
if "icon=" in arguments: |
|
text_or_icon = "icon" |
|
else: |
|
text_or_icon = "text" |
|
if ( |
|
"click" in active_block.code |
|
): |
|
description = f"Clicking {text_or_icon}..." |
|
else: |
|
description = f"Mousing over {text_or_icon}..." |
|
elif action.startswith("computer.keyboard.write("): |
|
description = f"Typing {arguments}." |
|
elif action.startswith("computer.keyboard.hotkey("): |
|
description = f"Pressing {arguments}." |
|
elif action.startswith("computer.keyboard.press("): |
|
description = f"Pressing {arguments}." |
|
elif action == "computer.os.get_selected_text()": |
|
description = f"Getting selected text." |
|
|
|
if description: |
|
interpreter.computer.os.notify(description) |
|
|
|
if "start" in chunk: |
|
|
|
if not isinstance(active_block, CodeBlock): |
|
if active_block: |
|
active_block.end() |
|
active_block = CodeBlock() |
|
|
|
if active_block: |
|
active_block.refresh(cursor=render_cursor) |
|
|
|
|
|
if "active_block" in locals(): |
|
if active_block: |
|
active_block.end() |
|
active_block = None |
|
time.sleep(0.1) |
|
|
|
if not interactive: |
|
|
|
break |
|
|
|
except KeyboardInterrupt: |
|
|
|
if "active_block" in locals() and active_block: |
|
active_block.end() |
|
active_block = None |
|
|
|
if interactive: |
|
|
|
continue |
|
else: |
|
break |
|
except: |
|
if interpreter.debug: |
|
system_info(interpreter) |
|
raise |
|
|