Add multi-line chat and help menu (#152)
# PR Description Given that `arcade chat` is the entry point for all users, this PR makes the developer's first experience with Arcade a better experience. 1. Add multi-line & pasting support to `arcade chat`. To start a multi-line chat message, start your message with `"""`. To end the multi-line chat message, end your line with `"""`.  2. Added chat commands - `/?` displays all available chat commands that are available - `/show` shows all of the tools available. This uses `arcade show`. Currently no option to specify the toolkit, or tool. - `/clear` clears the chat conversation history of the current session - `/exit` exits the chat. (ctrl+c still works of course!) 
This commit is contained in:
parent
c02aee3f14
commit
026d518c6e
2 changed files with 105 additions and 5 deletions
|
|
@ -31,8 +31,10 @@ from arcade.cli.utils import (
|
||||||
delete_deprecated_config_file,
|
delete_deprecated_config_file,
|
||||||
get_eval_files,
|
get_eval_files,
|
||||||
get_tools_from_engine,
|
get_tools_from_engine,
|
||||||
|
get_user_input,
|
||||||
handle_chat_interaction,
|
handle_chat_interaction,
|
||||||
handle_tool_authorization,
|
handle_tool_authorization,
|
||||||
|
handle_user_command,
|
||||||
is_authorization_pending,
|
is_authorization_pending,
|
||||||
load_eval_suites,
|
load_eval_suites,
|
||||||
log_engine_health,
|
log_engine_health,
|
||||||
|
|
@ -270,16 +272,19 @@ def chat(
|
||||||
log_engine_health(client)
|
log_engine_health(client)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
console.print(f"\n[magenta][bold]User[/bold] ({user_email}):[/magenta] ")
|
console.print(
|
||||||
|
f"\n[magenta][bold]User[/bold] {user_email}: [/magenta]"
|
||||||
|
+ "([bold][default]/?[/default][/bold] for help)"
|
||||||
|
)
|
||||||
|
|
||||||
# Use input() instead of console.input() to leverage readline history
|
user_input = get_user_input()
|
||||||
user_input = input()
|
|
||||||
while not user_input.strip():
|
|
||||||
user_input = input()
|
|
||||||
|
|
||||||
# Add the input to history
|
# Add the input to history
|
||||||
readline.add_history(user_input)
|
readline.add_history(user_input)
|
||||||
|
|
||||||
|
if handle_user_command(user_input, history, host, port, force_tls, force_no_tls, show):
|
||||||
|
continue
|
||||||
|
|
||||||
history.append({"role": "user", "content": user_input})
|
history.append({"role": "user", "content": user_input})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import ipaddress
|
||||||
import os
|
import os
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from textwrap import dedent
|
||||||
from typing import Any, Callable, Union, cast
|
from typing import Any, Callable, Union, cast
|
||||||
from urllib.parse import urlencode, urlparse
|
from urllib.parse import urlencode, urlparse
|
||||||
|
|
||||||
|
|
@ -36,6 +38,13 @@ class OrderCommands(TyperGroup):
|
||||||
return list(self.commands) # get commands using self.commands
|
return list(self.commands) # get commands using self.commands
|
||||||
|
|
||||||
|
|
||||||
|
class ChatCommand(str, Enum):
|
||||||
|
HELP = "/?"
|
||||||
|
CLEAR = "/clear"
|
||||||
|
SHOW = "/show"
|
||||||
|
EXIT = "/exit"
|
||||||
|
|
||||||
|
|
||||||
def create_cli_catalog(
|
def create_cli_catalog(
|
||||||
toolkit: str | None = None,
|
toolkit: str | None = None,
|
||||||
show_toolkits: bool = False,
|
show_toolkits: bool = False,
|
||||||
|
|
@ -587,3 +596,89 @@ def delete_deprecated_config_file() -> None:
|
||||||
|
|
||||||
if os.path.exists(deprecated_config_file_path):
|
if os.path.exists(deprecated_config_file_path):
|
||||||
os.remove(deprecated_config_file_path)
|
os.remove(deprecated_config_file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_input() -> str:
|
||||||
|
"""
|
||||||
|
Get input from the user, handling multi-line input.
|
||||||
|
"""
|
||||||
|
MULTI_LINE_PROMPT = '"""'
|
||||||
|
user_input = input()
|
||||||
|
# Handle multi-line input
|
||||||
|
if user_input.startswith(MULTI_LINE_PROMPT):
|
||||||
|
user_input = user_input[len(MULTI_LINE_PROMPT) :]
|
||||||
|
|
||||||
|
while not user_input.endswith(MULTI_LINE_PROMPT):
|
||||||
|
line = input()
|
||||||
|
if not line:
|
||||||
|
print()
|
||||||
|
user_input += "\n" + line
|
||||||
|
|
||||||
|
user_input = user_input.rstrip(MULTI_LINE_PROMPT)
|
||||||
|
else:
|
||||||
|
# Handle single-line input
|
||||||
|
while not user_input.strip():
|
||||||
|
user_input = input()
|
||||||
|
|
||||||
|
return user_input.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def display_chat_help() -> None:
|
||||||
|
"""Display the help message for arcade chat."""
|
||||||
|
help_message = dedent(f"""\
|
||||||
|
[default]
|
||||||
|
Available Commands:
|
||||||
|
{ChatCommand.SHOW:<10} Show all available tools
|
||||||
|
{ChatCommand.CLEAR:<10} Clear the chat history
|
||||||
|
{ChatCommand.HELP:<10} Help for a command
|
||||||
|
{ChatCommand.EXIT:<10} Exit the chat
|
||||||
|
|
||||||
|
Use \"\"\" to begin & end a multi-line message[/default]
|
||||||
|
""")
|
||||||
|
console.print(help_message)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_user_command(
|
||||||
|
user_input: str,
|
||||||
|
history: list,
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
force_tls: bool,
|
||||||
|
force_no_tls: bool,
|
||||||
|
show: Callable,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Handle user commands during `arcade chat` and return True if a command was processed, otherwise False.
|
||||||
|
"""
|
||||||
|
if user_input == ChatCommand.HELP:
|
||||||
|
display_chat_help()
|
||||||
|
return True
|
||||||
|
elif user_input == ChatCommand.EXIT:
|
||||||
|
raise KeyboardInterrupt
|
||||||
|
elif user_input == ChatCommand.CLEAR:
|
||||||
|
console.print("Chat history cleared.", style="bold green")
|
||||||
|
history.clear()
|
||||||
|
return True
|
||||||
|
elif user_input == ChatCommand.SHOW:
|
||||||
|
show(
|
||||||
|
toolkit=None,
|
||||||
|
tool=None,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
force_tls=force_tls,
|
||||||
|
force_no_tls=force_no_tls,
|
||||||
|
debug=False,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def parse_user_command(user_input: str) -> ChatCommand | None:
|
||||||
|
"""
|
||||||
|
Parse the user command and return the corresponding ChatCommand enum.
|
||||||
|
Returns None if the input is not a valid chat command.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return ChatCommand(user_input)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue