shell_gpt
shell_gpt copied to clipboard
Added support to pull from stdin if provided.
Added the ability for it to work with info piped in from stdin with and without an accompanying prompt.
Currently using the editor
argument will replace the incoming prompt, so anything that was piped in with stdin will be ignored.
In support of #35 and #80 .
Suggestion: Allow -
(dash) argument to force input from stdin, even if the terminal is a tty.
Motivation:
- I might actually want to input from stdin from my bash prompt. For example, to type a short message using symbols like
'
and$
and?
which can be troublesome at the bash prompt. But I don't want to go the full route of using an editor, which may be slightly slower, requiring loadup time and a save-and-exit step. -
-
is a pretty standard argument in Unix for this purpose (seegzip
,tar
andcat
). It can help in scripts to make it explicit when piping is being used. And it also allows for tricks like:filename="-"
or in this caseprompt="-"
Disdvantages:
- It would prevent users from sending a simple
-
prompt as part of a conversation. In fact it could interrupt or confuse them. - ... others ...?
Alternatives:
- We could consider making
-
the only way to getsgpt
to read from stdin. Not sure if that's desirable or not.
Possible implementation: (Can be applied with patch -p1 < file.txt
)
diff --git a/sgpt/app.py b/sgpt/app.py
index f9ac77b..764b22c 100644
--- a/sgpt/app.py
+++ b/sgpt/app.py
@@ -67,6 +67,7 @@ def main(
cache: bool = typer.Option(True, help="Cache completion results."),
animation: bool = typer.Option(True, help="Typewriter animation."),
spinner: bool = typer.Option(True, help="Show loading spinner during API request."),
+ dash: bool = typer.Option(False, "-", help="Read from stdin."),
) -> None:
if list_chat:
echo_chat_ids()
@@ -76,11 +77,11 @@ def main(
return
if not prompt and not editor:
- if not sys.stdin.isatty():
+ if dash or not sys.stdin.isatty():
prompt = sys.stdin.read()
else:
raise MissingParameter(param_hint="PROMPT", param_type="string")
- elif prompt and not sys.stdin.isatty():
+ elif prompt and (dash or not sys.stdin.isatty()):
stdin_data = sys.stdin.read()
prompt = f"{stdin_data.strip()}\n{prompt}"
I'm not sure this is working as intended. I applied the patch and there seems to be some weird edge case interactions. I'm also not sure how this is supposed to be used as I have never used the -
argument with other utilities.
The first thing that I found weird was when adding the -
as a parameter typer didn't add a reference to --dash
as an option.
$ sgpt --help
Usage: sgpt [OPTIONS] [PROMPT]
╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ prompt [PROMPT] The prompt to generate completions for. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --temperature FLOAT RANGE [0.0<=x<=1.0] Randomness of generated output. [default: 1.0] │
│ --top-probability FLOAT RANGE [0.1<=x<=1.0] Limits highest probable tokens (words). [default: 1.0] │
│ --chat TEXT Follow conversation with id (chat mode). [default: None] │
│ --show-chat TEXT Show all messages from provided chat id. [default: None] │
│ --list-chat --no-list-chat List all existing chat ids. [default: no-list-chat] │
│ --shell -s Provide shell command as output. │
│ --execute -e Will execute --shell command. │
│ --code --no-code Provide code as output. [default: no-code] │
│ --editor --no-editor Open $EDITOR to provide a prompt. [default: no-editor] │
│ --cache --no-cache Cache completion results. [default: cache] │
│ --animation --no-animation Typewriter animation. [default: animation] │
│ --spinner --no-spinner Show loading spinner during API request. [default: spinner] │
│ - Read from stdin. │
│ --help Show this message and exit. │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
$ sgpt -
works, and simply submits a blank API request, and also piping in anything results in an api request with whatever was coming in from stdin.
But
$ sgpt - "Test"
Usage: sgpt [OPTIONS] [PROMPT]
Try 'sgpt --help' for help.
╭─ Error ──────────────────────────────────────────────────────────────────────────────────╮
│ Got unexpected extra argument (Test) │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
So I added a "--dash"
to the added option line like:
dash: bool = typer.Option(False, "--dash", "-", help="Read from stdin."),
And now the help is
$ sgpt --help
Usage: sgpt [OPTIONS] [PROMPT]
╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ prompt [PROMPT] The prompt to generate completions for. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --temperature FLOAT RANGE [0.0<=x<=1.0] Randomness of generated output. │
│ [default: 1.0] │
│ --top-probability FLOAT RANGE [0.1<=x<=1.0] Limits highest probable tokens (words). │
│ [default: 1.0] │
│ --chat TEXT Follow conversation with id (chat mode). │
│ [default: None] │
│ --show-chat TEXT Show all messages from provided chat id. │
│ [default: None] │
│ --list-chat --no-list-chat List all existing chat ids. │
│ [default: no-list-chat] │
│ --shell -s Provide shell command as output. │
│ --execute -e Will execute --shell command. │
│ --code --no-code Provide code as output. [default: no-code] │
│ --editor --no-editor Open $EDITOR to provide a prompt. │
│ [default: no-editor] │
│ --cache --no-cache Cache completion results. [default: cache] │
│ --animation --no-animation Typewriter animation. [default: animation] │
│ --spinner --no-spinner Show loading spinner during API request. │
│ [default: spinner] │
│ --dash - Read from stdin. │
│ --help Show this message and exit. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Some minor tests
Now I can execute: $ sgpt --dash "Test"
but it just hangs.
$ echo "test2" | sgpt -
works as expected
$ echo "test2" | sgpt "test3"
works as expected
$ echo "test2" | sgpt - "test3"
gives extra argument error
$ sgpt - "test3"
gives extra argument error
Conclusion
Could you provide some example prompt / responses so I can get a better idea of what you are working towards and its purpose?
Thanks for testing. I'm sorry, I think my patch was bad. Originally I had a typo dash: bool = typer.Option(True
which should of course default to False
. So I thought it was working when it wasn't.
After I fixed that, and added logging, I found that passing -
was never picked up by typer! So I think instead we need to manually detect when the prompt is -
Here is my revised patch: https://github.com/lpurdy01/shell_gpt/pull/2
-
is not really called dash
. It is sometimes (but not always) paired with --stdin
. I have renamed it. Sorry for the confusion!
An example where -
is needed because I want to pipe, but in this environment stdin is a TTY:
# Get input from stdin, so I don't need to worry about escaping symbols
$ sgpt -
It is difficult to pass the raw symbols ` and ' and " and ? from a bash prompt. What other symbols are difficult to pass?
<CTRL-D>
(It looks like interactive mode will cover this use-case in future.)
Some examples where -
is not needed, but useful:
# The explcitl - may help readers to see how sgpt is behaving
$ ( echo 'Do you see an errors in this logfile?' ; tail -n 50 /var/log/Xorg.0.log ) | sgpt -
# Imagine we are in a shellscript
sgpt $extra_opts "$prompt"
# The caller could set prompt to a text string, or to "-" if they want to pass the prompt on stdin.
A case we may want to test: (test results are now in the review comments)
# A cronjob. stdin may not be a TTY, but that doesn't mean we want to read from stdin!
# My patch handles this case only reading from stdin if - or --stdin was specified
# Once a day, fetch a quote, and store it in the servers Message of the Day
0 0 * * * sgpt --no-cache "Show me a profound one-line quote, with the author's name" > /etc/motd
I didn't see this and wrote my own implementation and pull request (#93).
I used select
to determine if stdin is readable, otherwise stdin is ignored. It works from a TTY as well as a cron job.
It seems like both PRs handle cronjobs the same way. Both read stdin and add it to the prompt, even if nothing is being piped into stdin!
Fortunately Python's stdin.read()
is non-blocking, so it just returns ""
. The outcome is a \n
added to the prompt (either before or after), which is relatively harmless.
Thank you for the PR, since we are rolling out new versions of sgpt
3-4 times per week, feature branches are getting outdated very fast, this feature was implemented in my branch and merged today https://github.com/TheR1D/shell_gpt/pull/163