shell_gpt icon indicating copy to clipboard operation
shell_gpt copied to clipboard

Added support to pull from stdin if provided.

Open lpurdy01 opened this issue 1 year ago • 5 comments

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 .

lpurdy01 avatar Mar 23 '23 06:03 lpurdy01

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 (see gzip, tar and cat). 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 case prompt="-"

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 get sgpt 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}"

joeytwiddle avatar Mar 23 '23 09:03 joeytwiddle

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?

lpurdy01 avatar Mar 24 '23 01:03 lpurdy01

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

joeytwiddle avatar Mar 24 '23 08:03 joeytwiddle

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.

Shakahs avatar Mar 24 '23 14:03 Shakahs

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.

joeytwiddle avatar Mar 25 '23 06:03 joeytwiddle

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

TheR1D avatar Apr 10 '23 15:04 TheR1D