Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement non-blocking CLI history auto saver #1156

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 4 additions & 30 deletions interpreter/terminal_interface/terminal_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@
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
Expand All @@ -27,21 +20,7 @@
from .utils.find_image_path import find_image_path
from .utils.cli_input import cli_input

# Add examples to the readline history
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:
# If they don't have readline, that's fine
pass
from .utils.history import history_autosaver


def terminal_interface(interpreter, message):
Expand Down Expand Up @@ -72,17 +51,12 @@ def terminal_interface(interpreter, message):
active_block = None
voice_subprocess = None

history = history_autosaver() # Will stop when goes out of context
while True:
if interactive:
### This is the primary input for Open Interpreter.
message = cli_input("> ").strip() if interpreter.multi_line else input("> ").strip()

try:
# This lets users hit the up arrow key for past messages
readline.add_history(message)
except:
# If the user doesn't have readline (may be the case on windows), that's fine
pass
message = cli_input("> ", interpreter.multi_line).strip()
history.add(message) # Maybe move this in cli_input?

if isinstance(message, str):
# This is for the terminal interface being used as a CLI — messages are strings.
Expand Down
4 changes: 3 additions & 1 deletion interpreter/terminal_interface/utils/cli_input.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
def cli_input(prompt: str = "") -> str:
def cli_input(prompt: str = "", multi_line=False) -> str:
if not multi_line:
return input(prompt)
start_marker = "```"
end_marker = "```"
message = input(prompt)
Expand Down
77 changes: 77 additions & 0 deletions interpreter/terminal_interface/utils/history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import logging
import os
import queue
import threading

from .oi_dir import oi_dir

log = logging.getLogger(__name__)


# We probably want to decouple HistoryAutosave from oi_dir
def history_autosaver(hist_filepath=os.path.join(oi_dir, "terminal.hist")):
try:
import readline

class HistoryAutosaver:
# Messages queue
_q: queue.Queue
_thread: threading.Thread

def __init__(self):
self._q = queue.Queue()
self._run()

def add(self, msg, blocking=False):
if blocking:
readline.write_history_file(hist_filepath)
else:
self._q.put(msg)

def _load(self):
try:
readline.read_history_file(hist_filepath)
except FileNotFoundError:
pass

def _run(self):
readline.set_auto_history(True) # Maybe redundant
self._thread = threading.Thread(target=self._loop, daemon=True)
self._thread.start()

def _loop(self):
readline.read_history_file(hist_filepath)
while True:
log.debug("Waiting for history to write")
msg = self._q.get()
if msg is None:
break
try:
readline.append_history_file(1, hist_filepath)
except FileNotFoundError:
readline.write_history_file(hist_filepath)
log.debug("History written to " + hist_filepath)

def __del__(self):
# Is this redundant?
try:
log.debug("Closing history manager")
self._thread.join()
except:
pass

except ImportError:
log.warning("readline module not found, history autosave disabled")

class HistoryAutosaver:
pass

return HistoryAutosaver()


if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
handle = history_autosaver()
while True:
cmd = input("")
handle.add(cmd)