1414import subprocess
1515from threading import Thread as thr
1616from functools import wraps
17+
18+ # rich
19+
1720from rich .panel import Panel
1821from rich .style import Style
1922from rich .markdown import Markdown
2225from rich .table import Table
2326from rich .prompt import Prompt
2427from rich .progress import Progress
28+
2529from typing import Iterable
30+
31+ #pytgpt
32+
2633from pytgpt .utils import Optimizers
2734from pytgpt .utils import default_path
2835from pytgpt .utils import AwesomePrompts
2936from pytgpt .utils import RawDog
3037from pytgpt .imager import Imager
3138from pytgpt .imager import Prodia
3239from pytgpt .utils import Audio
40+ from pytgpt .utils import suggest_query
41+
3342from WebChatGPT .console import chat as webchatgpt
43+
44+ # colorama
3445from colorama import Fore
3546from colorama import init as init_colorama
47+
3648from dotenv import load_dotenv
3749
50+ # prompt-toolkit
51+ from prompt_toolkit import PromptSession
52+ from prompt_toolkit .completion import ThreadedCompleter , Completer , Completion
53+ from prompt_toolkit .document import Document
54+
3855init_colorama (autoreset = True )
3956
4057load_dotenv () # loads .env variables
@@ -329,6 +346,40 @@ def main(*args, **kwargs):
329346
330347 return decorator
331348
349+ class CustomCompleter (Completer ):
350+ """Suggests query based on user prompts"""
351+
352+ def __init__ (
353+ self ,
354+ caller : object ,
355+ suggestions_limit : int = 15 ,
356+ special_commands : list [str ] = [],
357+ ):
358+ self .suggestions_limit = suggestions_limit
359+ self .caller = caller
360+ self .special_commands = special_commands
361+
362+ def get_completions (self , document : Document , complete_event ):
363+ word = document .text
364+ if word and self .suggestions_limit > 0 and not word .startswith ("./" ):
365+ completions = []
366+ first_word = word .strip ().split (" " )[0 ]
367+ if first_word in self .special_commands :
368+ completions .append (
369+ Completion (
370+ f"{ first_word } [RESERVED] "
371+ + getattr (self .caller , f"do_{ first_word } " ).__doc__
372+ )
373+ )
374+ return completions
375+ for count , suggestion in enumerate (
376+ suggest_query (word , timeout = 10 , die_silently = True ),
377+ start = 1 ):
378+ completions .append (Completion (suggestion , start_position = - len (word )))
379+ if count >= self .suggestions_limit :
380+ break
381+ return completions
382+ return []
332383
333384class Main (cmd .Cmd ):
334385 intro = (
@@ -361,6 +412,8 @@ def __init__(
361412 internal_exec = False ,
362413 confirm_script = False ,
363414 interpreter = "python" ,
415+ suggestions_limit = 15 ,
416+ non_interactive = False ,
364417 * args ,
365418 ** kwargs ,
366419 ):
@@ -772,6 +825,22 @@ def __init__(
772825 self .read_aloud = False
773826 self .read_aloud_voice = "Brian"
774827 self .path_to_last_response_audio = None
828+ if not non_interactive :
829+ self .completer_session = PromptSession (
830+ "" ,
831+ completer = ThreadedCompleter (
832+ CustomCompleter (
833+ self ,
834+ suggestions_limit ,
835+ [
836+ "cd" , "copy_this" , "h" , "last_response" , "rawdog" ,
837+ "settings" , "with_copied" ,
838+ "clear" , "exec" , "help" , "load" , "reread" , "shell" ,
839+ "code" , "exit" , "history" , "new_intro" , "reset" , "sys" ,
840+ ],
841+ )
842+ ),
843+ )
775844 self .__init_time = time .time ()
776845 self .__start_time = time .time ()
777846 self .__end_time = time .time ()
@@ -802,7 +871,7 @@ def find_range(start, end, hms: bool = False):
802871 f"~[`{ Fore .LIGHTWHITE_EX } 🕒{ Fore .BLUE } { current_time } -`"
803872 f"{ Fore .LIGHTWHITE_EX } 💻{ Fore .RED } { find_range (self .__init_time , time .time (), True )} -`"
804873 f"{ Fore .LIGHTWHITE_EX } ⚡{ Fore .YELLOW } { find_range (self .__start_time , self .__end_time )} s]`"
805- f"\n ╰─>"
874+ # f"\n╰─>"
806875 )
807876 whitelist = ["[" , "]" , "~" , "-" , "(" , ")" ]
808877 for character in whitelist :
@@ -815,8 +884,70 @@ def find_range(start, end, hms: bool = False):
815884 f"~[🕒{ current_time } "
816885 f"-💻{ find_range (self .__init_time , time .time (), True )} "
817886 f"-⚡{ find_range (self .__start_time , self .__end_time )} s]"
818- "\n ╰─>"
887+ # "\n╰─>"
819888 )
889+ def cmdloop (self , intro = None ):
890+ """Repeatedly issue a prompt, accept input, parse an initial prefix
891+ off the received input, and dispatch to action methods, passing them
892+ the remainder of the line as argument.
893+
894+ """
895+
896+ self .preloop ()
897+ if self .use_rawinput and self .completekey :
898+ try :
899+ import readline
900+
901+ self .old_completer = readline .get_completer ()
902+ readline .set_completer (self .complete )
903+ if hasattr (readline , "backend" ) and readline .backend == "editline" :
904+ if self .completekey == "tab" :
905+ # libedit uses "^I" instead of "tab"
906+ command_string = "bind ^I rl_complete"
907+ else :
908+ command_string = f"bind { self .completekey } rl_complete"
909+ else :
910+ command_string = f"{ self .completekey } : complete"
911+ readline .parse_and_bind (command_string )
912+ except ImportError :
913+ pass
914+ try :
915+ if intro is not None :
916+ self .intro = intro
917+ if self .intro :
918+ self .stdout .write (str (self .intro ) + "\n " )
919+ stop = None
920+ while not stop :
921+ if self .cmdqueue :
922+ line = self .cmdqueue .pop (0 )
923+ else :
924+ if self .use_rawinput :
925+ try :
926+ print (self .prompt , end = "" )
927+ line = self .completer_session .prompt ("\n ╰─>" )
928+ except EOFError :
929+ line = "EOF"
930+ else :
931+ self .stdout .write (self .prompt )
932+ self .stdout .flush ()
933+ line = self .stdin .readline ()
934+ if not len (line ):
935+ line = "EOF"
936+ else :
937+ line = line .rstrip ("\r \n " )
938+ line = self .precmd (line )
939+ stop = self .onecmd (line )
940+ stop = self .postcmd (stop , line )
941+ self .postloop ()
942+ finally :
943+ if self .use_rawinput and self .completekey :
944+ try :
945+ import readline
946+
947+ readline .set_completer (self .old_completer )
948+ except ImportError :
949+ pass
950+
820951
821952 def output_bond (
822953 self ,
@@ -1244,7 +1375,7 @@ def do_sys(self, line):
12441375 def do_exit (self , line ):
12451376 """Quit this program"""
12461377 if click .confirm ("Are you sure to exit" ):
1247- click .secho ("Okay Goodbye!" , fg = "yellow" )
1378+ click .secho ("^-^ Okay Goodbye!" , fg = "yellow" )
12481379 return True
12491380
12501381
@@ -1422,6 +1553,13 @@ class ChatInteractive:
14221553 "for advanced g4f providers test"
14231554 ),
14241555 )
1556+ @click .option (
1557+ '-sl' ,
1558+ "--suggestions-limit" ,
1559+ type = click .INT ,
1560+ help = "Prompt suggestions limit - 0 to disable suggestion" ,
1561+ default = 15 ,
1562+ )
14251563 @click .option (
14261564 "-vo" ,
14271565 "--vertical-overflow" ,
@@ -1530,6 +1668,7 @@ def interactive(
15301668 awesome_prompt ,
15311669 proxy_path ,
15321670 provider ,
1671+ suggestions_limit ,
15331672 vertical_overflow ,
15341673 whole ,
15351674 quiet ,
@@ -1570,6 +1709,7 @@ def interactive(
15701709 internal_exec = internal_exec ,
15711710 confirm_script = confirm_script ,
15721711 interpreter = interpreter ,
1712+ suggestions_limit = suggestions_limit
15731713 )
15741714 busy_bar .spin_index = busy_bar_index
15751715 bot .code_theme = code_theme
@@ -1869,6 +2009,7 @@ def generate(
18692009 internal_exec = internal_exec ,
18702010 confirm_script = confirm_script ,
18712011 interpreter = interpreter ,
2012+ non_interactive = True
18722013 )
18732014 prompt = prompt if prompt else ""
18742015 copied_placeholder = "{{copied}}"
0 commit comments