-
Notifications
You must be signed in to change notification settings - Fork 51
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
feat: add execute method to commands #17
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
4d65ce1
tips for ticket
lukealvoeiro 98188fc
Merge branch 'main' into lalvoeiro/execute-command
lukealvoeiro f46fb53
feat: file command returns query
lukealvoeiro c26a5c9
feat: allow " escaped commands
lukealvoeiro 491449b
feat: improve tests
lukealvoeiro 026f801
feat: execute the command
lukealvoeiro 0b801b7
fix: save session name tests
lukealvoeiro 3fb42db
fix: all tests
lukealvoeiro b4f7f6b
feat: include testing dependency
lukealvoeiro d50249b
chore: some small renames
lukealvoeiro 1d20e15
chore: docstring improvements
lukealvoeiro 0a150cc
fix: remove async mark
lukealvoeiro 7f2ccbf
fix: unused import
lukealvoeiro 682aa1e
feat: comments and don't assume next class is a value
lukealvoeiro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,84 @@ | ||
from typing import Optional | ||
|
||
from prompt_toolkit import PromptSession | ||
from prompt_toolkit.document import Document | ||
from prompt_toolkit.formatted_text import FormattedText | ||
from prompt_toolkit.validation import DummyValidator | ||
|
||
from goose.cli.prompt.create import create_prompt | ||
from goose.cli.prompt.lexer import PromptLexer | ||
from goose.cli.prompt.prompt_validator import PromptValidator | ||
from goose.cli.prompt.user_input import PromptAction, UserInput | ||
from goose.command import get_commands | ||
|
||
|
||
class GoosePromptSession: | ||
def __init__(self, prompt_session: PromptSession) -> None: | ||
self.prompt_session = prompt_session | ||
def __init__(self) -> None: | ||
# instantiate the commands available in the prompt | ||
self.commands = dict() | ||
command_plugins = get_commands() | ||
for command, command_cls in command_plugins.items(): | ||
self.commands[command] = command_cls() | ||
self.main_prompt_session = create_prompt(self.commands) | ||
self.text_prompt_session = PromptSession() | ||
|
||
@staticmethod | ||
def create_prompt_session() -> "GoosePromptSession": | ||
return GoosePromptSession(create_prompt()) | ||
def get_message_after_commands(self, message: str) -> str: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some comments to make the flow of logic clearer to the uninitiated but feel free to ignore! |
||
lexer = PromptLexer(command_names=list(self.commands.keys())) | ||
doc = Document(message) | ||
lines = [] | ||
# iterate through each line of the document | ||
for line_num in range(len(doc.lines)): | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
classes_in_line = lexer.lex_document(doc)(line_num) | ||
line_result = [] | ||
i = 0 | ||
while i < len(classes_in_line): | ||
# if a command is found and it is not the last part of the line | ||
if classes_in_line[i][0] == "class:command" and i + 1 < len(classes_in_line): | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# extract the command name | ||
command_name = classes_in_line[i][1].strip("/").strip(":") | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# get the value following the command | ||
if classes_in_line[i + 1][0] == "class:parameter": | ||
command_value = classes_in_line[i + 1][1] | ||
else: | ||
command_value = "" | ||
|
||
# execute the command with the given argument, expecting a return value | ||
value_after_execution = self.commands[command_name].execute(command_value, message) | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# if the command returns None, raise an error - this should never happen | ||
# since the command should always return a string | ||
if value_after_execution is None: | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
raise ValueError(f"Command {command_name} returned None") | ||
|
||
# append the result of the command execution to the line results | ||
line_result.append(value_after_execution) | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
i += 1 | ||
|
||
# if the part is plain text, just append it to the line results | ||
elif classes_in_line[i][0] == "class:text": | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
line_result.append(classes_in_line[i][1]) | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
i += 1 | ||
|
||
# join all processed parts of the current line and add it to the lines list | ||
lines.append("".join(line_result)) | ||
|
||
# join all processed lines into a single string with newline characters and return | ||
return "\n".join(lines) | ||
lukealvoeiro marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def get_user_input(self) -> "UserInput": | ||
try: | ||
text = FormattedText([("#00AEAE", "G❯ ")]) # Define the prompt style and text. | ||
message = self.prompt_session.prompt(text, validator=PromptValidator(), validate_while_typing=False) | ||
message = self.main_prompt_session.prompt(text, validator=PromptValidator(), validate_while_typing=False) | ||
if message.strip() in ("exit", ":q"): | ||
return UserInput(PromptAction.EXIT) | ||
|
||
message = self.get_message_after_commands(message) | ||
return UserInput(PromptAction.CONTINUE, message) | ||
except (EOFError, KeyboardInterrupt): | ||
return UserInput(PromptAction.EXIT) | ||
|
||
def get_save_session_name(self) -> Optional[str]: | ||
return self.prompt_session.prompt( | ||
return self.text_prompt_session.prompt( | ||
"Enter a name to save this session under. A name will be generated for you if empty: ", | ||
validator=DummyValidator(), | ||
) | ||
).strip(" ") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,55 @@ | ||
from unittest.mock import patch | ||
|
||
from prompt_toolkit import PromptSession | ||
import pytest | ||
from goose.cli.prompt.goose_prompt_session import GoosePromptSession | ||
from goose.cli.prompt.user_input import PromptAction, UserInput | ||
|
||
|
||
@pytest.fixture | ||
def mock_prompt_session(): | ||
with patch("prompt_toolkit.PromptSession") as mock_prompt_session: | ||
with patch("goose.cli.prompt.goose_prompt_session.PromptSession") as mock_prompt_session: | ||
yield mock_prompt_session | ||
|
||
|
||
def test_get_save_session_name(mock_prompt_session): | ||
mock_prompt_session.prompt.return_value = "my_session" | ||
goose_prompt_session = GoosePromptSession(mock_prompt_session) | ||
mock_prompt_session.return_value.prompt.return_value = "my_session" | ||
goose_prompt_session = GoosePromptSession() | ||
|
||
assert goose_prompt_session.get_save_session_name() == "my_session" | ||
|
||
|
||
def test_get_user_input_to_continue(mock_prompt_session): | ||
mock_prompt_session.prompt.return_value = "input_value" | ||
goose_prompt_session = GoosePromptSession(mock_prompt_session) | ||
def test_get_save_session_name_with_space(mock_prompt_session): | ||
mock_prompt_session.return_value.prompt.return_value = "my_session " | ||
goose_prompt_session = GoosePromptSession() | ||
|
||
user_input = goose_prompt_session.get_user_input() | ||
assert goose_prompt_session.get_save_session_name() == "my_session" | ||
|
||
|
||
def test_get_user_input_to_continue(): | ||
with patch.object(PromptSession, "prompt", return_value="input_value"): | ||
goose_prompt_session = GoosePromptSession() | ||
|
||
user_input = goose_prompt_session.get_user_input() | ||
|
||
assert user_input == UserInput(PromptAction.CONTINUE, "input_value") | ||
assert user_input == UserInput(PromptAction.CONTINUE, "input_value") | ||
|
||
|
||
@pytest.mark.parametrize("exit_input", ["exit", ":q"]) | ||
def test_get_user_input_to_exit(exit_input, mock_prompt_session): | ||
mock_prompt_session.prompt.return_value = exit_input | ||
goose_prompt_session = GoosePromptSession(mock_prompt_session) | ||
with patch.object(PromptSession, "prompt", return_value=exit_input): | ||
goose_prompt_session = GoosePromptSession() | ||
|
||
user_input = goose_prompt_session.get_user_input() | ||
user_input = goose_prompt_session.get_user_input() | ||
|
||
assert user_input == UserInput(PromptAction.EXIT) | ||
assert user_input == UserInput(PromptAction.EXIT) | ||
|
||
|
||
@pytest.mark.parametrize("error", [EOFError, KeyboardInterrupt]) | ||
def test_get_user_input_to_exit_when_error_occurs(error, mock_prompt_session): | ||
mock_prompt_session.prompt.side_effect = error | ||
goose_prompt_session = GoosePromptSession(mock_prompt_session) | ||
with patch.object(PromptSession, "prompt", side_effect=error): | ||
goose_prompt_session = GoosePromptSession() | ||
|
||
user_input = goose_prompt_session.get_user_input() | ||
user_input = goose_prompt_session.get_user_input() | ||
|
||
assert user_input == UserInput(PromptAction.EXIT) | ||
assert user_input == UserInput(PromptAction.EXIT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is an old function but can we get a docstring since it's part of your PR 🙏
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for sure, good call!