Skip to content

Commit 41831e3

Browse files
committed
- feat: Add query suggestions in interactive
- feat: Add query suggestions test - chore: Bump version to 0.7.8 - chore: Update requirements - chore: Update python version in workflows
1 parent e5453d4 commit 41831e3

File tree

12 files changed

+200
-17
lines changed

12 files changed

+200
-17
lines changed

.github/workflows/binaries-minimal-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Set up Python
3232
uses: actions/setup-python@v3
3333
with:
34-
python-version: '3.12'
34+
python-version: '3.13'
3535
- name: Install dependencies and python-tgpt
3636
run: |
3737
python -m pip install --upgrade pip pyinstaller pillow

.github/workflows/binaries-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Set up Python
3232
uses: actions/setup-python@v3
3333
with:
34-
python-version: '3.12'
34+
python-version: '3.13'
3535
- name: Install dependencies and python-tgpt
3636
run: |
3737
python -m pip install --upgrade pip pyinstaller pillow

.github/workflows/debian-minimal-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ${{ matrix.os }}
1313
strategy:
1414
matrix:
15-
python-version: ["3.12"]
15+
python-version: ["3.13"]
1616
include:
1717
- os: ubuntu-latest
1818
artifact_name: pytgpt.deb
@@ -23,7 +23,7 @@ jobs:
2323
- name: Set up Python
2424
uses: actions/setup-python@v3
2525
with:
26-
python-version: '3.12'
26+
python-version: '3.13'
2727
- name: Install pip and pyinstaller
2828
run: python -m pip install --upgrade pip pyinstaller
2929
- name: Install python-tgpt

.github/workflows/debian-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ${{ matrix.os }}
1313
strategy:
1414
matrix:
15-
python-version: ["3.12"]
15+
python-version: ["3.13"]
1616
include:
1717
- os: ubuntu-latest
1818
artifact_name: pytgpt.deb
@@ -23,7 +23,7 @@ jobs:
2323
- name: Set up Python
2424
uses: actions/setup-python@v3
2525
with:
26-
python-version: '3.12'
26+
python-version: '3.13'
2727
- name: Install pip and pyinstaller
2828
run: python -m pip install --upgrade pip pyinstaller
2929
- name: Install python-tgpt

.github/workflows/python-package.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ name: Python package
55

66
on:
77
push:
8-
branches: [ "master" ]
8+
branches: [ "main", "master" ]
99
pull_request:
10-
branches: [ "master" ]
10+
branches: [ "main", "master" ]
1111

1212
jobs:
1313
build:
@@ -16,7 +16,7 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
python-version: ["3.12"]
19+
python-version: [ "3.13", "3.12", "3.11", "3.10" ]
2020

2121
steps:
2222
- uses: actions/checkout@v3

.github/workflows/python-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Set up Python
2626
uses: actions/setup-python@v3
2727
with:
28-
python-version: '3.12'
28+
python-version: '3.13'
2929
- name: Install dependencies
3030
run: |
3131
python -m pip install --upgrade pip

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ brotli==1.1.0
1717
Helpingai_T2-fork==0.3.2
1818
fastapi[all]==0.115.4
1919
python-vlc>=3.0.20
20-
httpx==0.27.2
20+
httpx==0.27.2
21+
prompt-toolkit==3.0.48

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"colorama==0.4.6",
2828
"g4f>=0.2.6.1",
2929
"python-dotenv==1.0.0",
30+
"prompt-toolkit==3.0.48",
3031
]
3132

3233
api = [
@@ -56,7 +57,7 @@
5657

5758
setup(
5859
name="python-tgpt",
59-
version="0.7.7",
60+
version="0.7.8",
6061
license="MIT",
6162
author="Smartwa",
6263
maintainer="Smartwa",

src/pytgpt/auto/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pytgpt.gpt4free import GPT4FREE, AsyncGPT4FREE
99
from pytgpt.gpt4free.utils import TestProviders
1010
from pytgpt.auto.errors import AllProvidersFailure
11+
from pytgpt.utils import Conversation
1112
from pytgpt.async_providers import tgpt_mapper as async_provider_map
1213
from typing import AsyncGenerator
1314

@@ -77,7 +78,7 @@ def last_response(self) -> dict[str, Any]:
7778
return self.provider.last_response
7879

7980
@property
80-
def conversation(self) -> object:
81+
def conversation(self) -> Conversation:
8182
return self.provider.conversation
8283

8384
def ask(
@@ -288,7 +289,7 @@ def last_response(self) -> dict[str, Any]:
288289
return self.provider.last_response
289290

290291
@property
291-
def conversation(self) -> object:
292+
def conversation(self) -> Conversation:
292293
return self.provider.conversation
293294

294295
async def ask(

src/pytgpt/console.py

Lines changed: 144 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
import subprocess
1515
from threading import Thread as thr
1616
from functools import wraps
17+
18+
# rich
19+
1720
from rich.panel import Panel
1821
from rich.style import Style
1922
from rich.markdown import Markdown
@@ -22,19 +25,33 @@
2225
from rich.table import Table
2326
from rich.prompt import Prompt
2427
from rich.progress import Progress
28+
2529
from typing import Iterable
30+
31+
#pytgpt
32+
2633
from pytgpt.utils import Optimizers
2734
from pytgpt.utils import default_path
2835
from pytgpt.utils import AwesomePrompts
2936
from pytgpt.utils import RawDog
3037
from pytgpt.imager import Imager
3138
from pytgpt.imager import Prodia
3239
from pytgpt.utils import Audio
40+
from pytgpt.utils import suggest_query
41+
3342
from WebChatGPT.console import chat as webchatgpt
43+
44+
# colorama
3445
from colorama import Fore
3546
from colorama import init as init_colorama
47+
3648
from 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+
3855
init_colorama(autoreset=True)
3956

4057
load_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

333384
class 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

Comments
 (0)