Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPT-3.5 #1365

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

GPT-3.5 #1365

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@ default:
* `git_push_force` – adds `--force-with-lease` to a `git push` (may conflict with `git_push_pull`);
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.

The following rule uses OpenAI ChatGPT. To enable it, you need to set the environment variable `THEFUCK_OPENAI_TOKEN=<OpenAI Token>` and pass in `--chatgpt [No. SUGGESTIONS >= 1]`:

- `chatgpt` &ndash; queries ChatGPT for suggestions. Arguments: `--chatgpt [No. SUGGESTIONS, default=0] --chatgpt-token [default=100] --chatgpt-model [default="gpt-3.5-turbo"]`.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very different from any other user interaction with The Fuck.

##### [Back to Contents](#contents)

## Creating your own rules
Expand Down
6 changes: 5 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#!/bin/sh
#!/bin/zsh
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be part of the pull request.


echo "Installation script is deprecated!"
echo "For installation instruction please visit https://github.com/nvbn/thefuck"
echo ""
echo "To install from sources run:"
echo "python setup.py build && python setup.py install"
python setup.py build && python setup.py install
Comment on lines +5 to +8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user comes to this point they know how to install things in their Python environment (or virtual environment). There's no intention of recommending anything at this point.

Also, this should not be part of the pull request.

1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ pypandoc
pytest-benchmark
pytest-docker-pexpect
twine
openai
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The introduction of a new hard dependency has huge consequences downstream. Not to mention this is a dependency for something optional.

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
' ({}.{} detected).'.format(*version))
sys.exit(-1)

VERSION = '3.32'
VERSION = '3.33'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new release is always decoupled of specific changes.


install_requires = ['psutil', 'colorama', 'six']
extras_require = {':python_version<"3.4"': ['pathlib2'],
Expand Down
4 changes: 3 additions & 1 deletion tests/test_argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ def _args(**override):
'help': False, 'version': False, 'debug': False,
'force_command': None, 'repeat': False,
'enable_experimental_instant_mode': False,
'shell_logger': None}
'shell_logger': None, 'chatgpt': 0,
'chatgpt_token': 100,
'chatgpt_model': 'gpt-3.5-turbo'}
args.update(override)
return args

Expand Down
2 changes: 1 addition & 1 deletion tests/test_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_from_env_with_DEFAULT(self, os_environ, settings):


def test_settings_from_args(settings):
settings.init(Mock(yes=True, debug=True, repeat=True))
settings.init(Mock(yes=True, debug=True, repeat=True, chatgpt=0, chatgpt_token=100))
assert not settings.require_confirmation
assert settings.debug
assert settings.repeat
Expand Down
19 changes: 19 additions & 0 deletions thefuck/argument_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,25 @@ def _add_arguments(self):
'-h', '--help',
action='store_true',
help='show this help message and exit')
self._parser.add_argument(
'-c', '--chatgpt',
type=int,
default=1,
help='number of ChatGPT suggestions. set to 0 to disable ChatGPT'
)
# todo Secret parameters only revealed with `thefuck --help --chatgpt`
# self._parser.add_argument(
# '-t', '--chatgpt-token',
# type=int,
# default=400,
# help='maximum ChatGPT tokens per query'
# )
# self._parser.add_argument(
# '-m', '--chatgpt-model',
# type=str,
# default="gpt-3.5-turbo",
# help='ChatGPT model'
# )
Comment on lines +46 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented code is dead code. Commented code should never be committed.

self._add_conflicting_arguments()
self._parser.add_argument(
'-d', '--debug',
Expand Down
4 changes: 4 additions & 0 deletions thefuck/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ def _settings_from_args(self, args):
from_args['debug'] = args.debug
if args.repeat:
from_args['repeat'] = args.repeat

from_args['chatgpt'] = args.chatgpt if args.chatgpt >= 0 else 1
from_args['chatgpt_token'] = args.chatgpt_token if args.chatgpt_token >= 0 else 400
from_args['chatgpt_model'] = args.chatgpt_model or "gpt-3.5-turbo"
return from_args


Expand Down
77 changes: 77 additions & 0 deletions thefuck/rules/chatgpt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import platform
import openai
import re
import os
from thefuck import logs
from thefuck.conf import settings


def _check_chatgpt(api_key: str = None) -> bool:
openai.api_key = os.getenv("THEFUCK_OPENAI_TOKEN") or os.getenv("OPENAI_API_KEY")
if settings["chatgpt"] > 0 and (api_key or openai.api_key):
return True
return False
Comment on lines +11 to +13
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if settings["chatgpt"] > 0 and (api_key or openai.api_key):
return True
return False
return settings["chatgpt"] > 0 and (api_key or openai.api_key)



enabled_by_default = _check_chatgpt()
logs.debug(f"ChatGPT enabled: {enabled_by_default}")

MAX_NUMBER = settings["chatgpt"] or 1
MAX_TOKENS = settings["chatgpt_token"] or 400
MODEL = settings["chatgpt_model"] or "gpt-3.5-turbo"


def match(command):
return _check_chatgpt()


def get_new_command(command):
result = _query_chatgpt(
command=command.script,
error=command.output,
explanation=False,
)
logs.debug(f"chatgpt result: {result}")
return result


def _query_chatgpt(
command: str,
error: str,
explanation: bool, # can be used to include explanations but not used yet
number: int = MAX_NUMBER,
model: str = MODEL,
max_tokens: int = MAX_TOKENS,
api_key: str = None,
Comment on lines +39 to +45
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nowhere else in The Fuck's code there's use of such syntax. This code would be very alien to the existing code base and therefore awckward to maintain, to say the least.

):
if api_key:
openai.api_key = api_key
elif openai.api_key is None:
return []

os_env = f"{platform.platform()}"
prompt = f"""
OS: `{os_env}`
Command: `{command}`
Error: `{error}`
Suggest {"one command" if number == 1 else f"{number} commands"} {"with" if explanation else "without"} explanation.
Commands:"""

logs.debug("chatgpt: " + prompt)

try:
response = openai.ChatCompletion.create(
model=model,
messages=[
{"role": "user", "content": prompt},
],
max_tokens=max_tokens,
)
content = response["choices"][0]["message"]["content"]
contents = [item.strip() for item in content.split("\n") if item.strip() != ""]
pattern = re.compile(r"^\d+\.\ *")
cleaned_contents = [re.sub(pattern, "", item).strip('`') for item in contents]
return cleaned_contents
Comment on lines +73 to +74
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return a generator instead. That's how The Fuck is designed.

except Exception as e:
logs.debug(f"chatgpt error: {e}")
return []