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,
|
||||
get_eval_files,
|
||||
get_tools_from_engine,
|
||||
get_user_input,
|
||||
handle_chat_interaction,
|
||||
handle_tool_authorization,
|
||||
handle_user_command,
|
||||
is_authorization_pending,
|
||||
load_eval_suites,
|
||||
log_engine_health,
|
||||
|
|
@ -270,16 +272,19 @@ def chat(
|
|||
log_engine_health(client)
|
||||
|
||||
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 = input()
|
||||
while not user_input.strip():
|
||||
user_input = input()
|
||||
user_input = get_user_input()
|
||||
|
||||
# Add the input to history
|
||||
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})
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ import ipaddress
|
|||
import os
|
||||
import webbrowser
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from typing import Any, Callable, Union, cast
|
||||
from urllib.parse import urlencode, urlparse
|
||||
|
||||
|
|
@ -36,6 +38,13 @@ class OrderCommands(TyperGroup):
|
|||
return list(self.commands) # get commands using self.commands
|
||||
|
||||
|
||||
class ChatCommand(str, Enum):
|
||||
HELP = "/?"
|
||||
CLEAR = "/clear"
|
||||
SHOW = "/show"
|
||||
EXIT = "/exit"
|
||||
|
||||
|
||||
def create_cli_catalog(
|
||||
toolkit: str | None = None,
|
||||
show_toolkits: bool = False,
|
||||
|
|
@ -587,3 +596,89 @@ def delete_deprecated_config_file() -> None:
|
|||
|
||||
if os.path.exists(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