Coder Social home page Coder Social logo

signebedi / gptty Goto Github PK

View Code? Open in Web Editor NEW
48.0 3.0 7.0 743 KB

ChatGPT wrapper in your TTY

License: MIT License

Python 100.00%
chatbot openai gpt-3 openai-api gpt-4 chatroom click gpt-35-turbo query shell tty chatgpt chatgpt-api package python gpt-4-turbo gpt-4-vision

gptty's Introduction

gptty

License: MIT PyPI version Downloads gptty tests Buy me a coffee

ChatGPT wrapper in your TTY

Note

This version supports gpt4 and gpt4-turbo!

About

gptty is a ChatGPT shell interface that allows you to (1) interact with ChatGPT in a manner similar to the web application, but without needing to rely on the web application's stability; (2) preserve context across chat sessions and structure your conversations however you want; (3) save local copies of your conversations for easy reference.

Use Cases

Perhaps you are a system admininstrator configuring a web server for your employer. You're accessing the system from a physical interface, with internet connection but no desktop environment or graphical user interface. While configuring the web server, you receive an inexplicable error that you redirect to a file, but don't want to have to jump through hoops to copy it to another system with a browser so you can look up the error. Instead, you install gptty and redirect the error to the chat client with commands like gptty query --tag error --question "$(cat app.error | tr '\n' ' ')" (which will get rid of line breaks for you) or cat app.error | xargs -d '\n' -I {} gptty query --tag error --question "{}" (which presumes that your error spans only a single line).

error troubleshooting example

Alternatively, you are a software developer or data scientist who wants to pipe data through ChatGPT, but wants to employ a highly abstract API to make these requests instead of familiarizing yourself intimately with the OpenAI API and its various language-specific wrappers. When you want to update your code base to use a different model, you want to be able to just modify a single config file and expect the query response format to remain consistent across various models.

Or maybe you are an enthusiast who wants to keep local copies of their conversations, or wants to exert more direct control over the categorization methods you employ for these conversations.

Model Support

OpenAI makes a number of models available through their API. [1] Currently, gptty supports Completions (davinci, curie) and ChatCompletions (gpt-3.5-turbo, gpt-4). All you need to do is specify the model name in your config (default is text-davinci-003), and the application will handle the rest.

Installation

You can install gptty on pip:

pip install gptty

You can also install from git:

cd ~/Code # replace this with whatever directory you want to use
git clone https://github.com/signebedi/gptty.git
cd gptty/

# now install the requirements
python3 -m venv venv
source venv/bin/activate
pip install -e .

Now, you can verify it is working by running gptty --help. If you experience an error, try configuring the app.

Configuration

gptty reads configuration settings from a file named gptty.ini, which the app expects to be located in the same directory that you are running gptty from unless you pass a custom config_file. The file uses the INI file format, which consists of sections, each with its own key-value pairs.

Key Type Default Value Description
api_key String "" Your API key for OpenAI's GPT service
org_id String "" Your organization ID for OpenAI's GPT service
your_name String "question" The name of the input prompt
gpt_name String "response" The name of the generated response
output_file String "output.txt" The name of the file where the output will be saved
model String "text-davinci-003" The name of the GPT model to use
temperature Float 0.0 The temperature to use for sampling
max_tokens Integer 250 The maximum number of tokens to generate for the response
max_context_length Integer 150 The maximum length of the input context
context_keywords_only Bool True Tokenize keywords to reduce API usage
preserve_new_lines Bool False Keep original formatting of response
verify_internet_endpoint String "google.com" Address to validate internet connection

You can modify the settings in the configuration file to suit your needs. If a key is not present in the configuration file, the default value will be used. The [main] section is used to specify the program's settings.

[main]
api_key=my_api_key

This repository provides a sample configuration file assets/gptty.ini.example that you can use as a starting point.

Usage

Chat

The chat feature provides an interactive chat interface to communicate with ChatGPT. You can ask questions and receive responses in real-time.

To start the chat interface, run gptty chat. You can also specify a custom configuration file path by running:

gptty chat --config_path /path/to/your/gptty.ini

Inside the chat interface, you can type your questions or commands directly. To view the list of available commands, type :help, which will show the following options.

Metacommand Description
:help Display a list of available commands and their descriptions.
:quit Exit ChatGPT.
:logs Display the current configuration settings.
:context[a:b] Display the context history, optionally specifying a range a and b. Under development

To use a command, simply type it into the command prompt and press Enter. For example, use the following command to display the current configuration settings in the terminal:

> :configs

api_key: SOME_KEY_HERE
org_id: org-SOME_CHARS_HERE
your_name: question
gpt_name: response
output_file: output.txt
model: text-davinci-003
temperature: 0.0
max_tokens: 250
max_context_length: 5000

You can type a question into the prompt anytime, and it will generate a response for you. If you'd like to share context across queries, see the context section below.

Query

The query feature allows you to submit a single or multiple questions to ChatGPT and receive the answers directly in the command line.

To use the query feature, run something like:

gptty query --question "What is the capital of France?" --question "What is the largest mammal?"

You can also provide an optional tag to categorize your query:

gptty query --question "What is the capital of France?" --tag "geography"

You can specify a custom configuration file path if needed:

gptty query --config_path /path/to/your/gptty.ini --question "What is the capital of France?"

Remember that gptty uses a configuration file (by default gptty.ini) to store settings like API keys, model configurations, and output file paths. Make sure you have a valid configuration file before running gptty commands.

Verbosity

By adding the --verbose tag at the end of your chat and query commands, the application will provide additional debug data, including token-counts for each request. This can be useful when you need to track API usage rates.

verbosity example

Additional Context

By adding the --additional_context [some_string_here] option to your query commands, the application will add any string you pass as further, outside context for your question.

JSON

By adding the --json tag at the end of your query commands, the application will skip writing human readable text to stdout, and instead write the questions and responses as json objects like [{"question":QUESTION_1, "response":RESPONSE_1},{"question":QUESTION_1, "response":RESPONSE_1},...].

json example

Quiet

By adding the --quiet tag at the end of your query commands, the application will skip writing anything to stdout, but will still write responses to the output_file designated in the application config file.

Context

Tagging text for context when using the chat and query subcommands in this app can help improve the accuracy of the generated responses. Here's how the app handles context with the chat subcommand:

  1. Identify the context of your question or statement.
  2. Assign a tag to that context. The tag can be a word or short phrase that describes the context like bananas or shakespeare.
  3. Include the tag in your input message by prefixing it with [tag]. For example, if the context of your question is "cooking," you can tag it as [cooking]. Make sure to use the same tag consistently for all related queries.
  4. The application will save your tagged question and response in the output file specified in the code output file.
  5. When asking subsequent questions on the same topic, provide the tag in your input message in order to retrieve the relevant context for the generated response.

Here is an example of what this might look like, using questions tagged as [shakespeare]. Notice how, in the second question, the name 'William Shakespeare' is not mentioned at all.

context example

When you are using the query subcommand, follow the same steps described above but, instead of prepending the text of your questions with your desired tag, use the --tag option to include the tag when submitting your query. For example, if the context of your question is "cooking," you can use:

gptty --question "some question" --tag cooking

The application will save your tagged question and response in the output file specified in the config file.

Scripting

You can automate the process of sending multiple questions to the gptty query command using a bash script. This can be particularly useful if you have a list of questions stored in a file, and you want to process them all at once. For example, let's say you have a file questions.txt with each question on a new line, like below.

What are the key differences between machine learning, deep learning, and artificial intelligence?
How do I choose the best programming language for a specific project or task?
Can you recommend some best practices for code optimization and performance improvement?
What are the essential principles of good software design and architecture?
How do I get started with natural language processing and text analysis in Python?
What are some popular Python libraries or frameworks for building web applications?
Can you suggest some resources to learn about data visualization and its implementation in Python?
What are some important concepts in cybersecurity, and how can I apply them to my projects?
How do I ensure that my machine learning models are fair, ethical, and unbiased?
Can you recommend strategies for staying up-to-date with the latest trends and advancements in technology and programming?

You can send each question from the questions.txt file to the gptty query command using the following bash one-liner:

xargs -d '\n' -I {} gptty query --question "{}" < questions.txt

question chain example

UniversalCompletion

The UniversalCompletion class provides a unified interface for interacting with OpenAI's language models, (mostly) abstracting away the specifics of whether the application is using the Completion or ChatCompletion mode. The main idea is to facilitate the creation, configuration, and management of the language models. Here is some example usage.

# First, import the UniversalCompletion class from the gptty library.
from gptty import UniversalCompletion

# Now, we instantiate a new UniversalCompletion object. 
# The 'api_key' parameter is your OpenAI API key, which you get when you sign up for the API.
# The 'org_id' parameter is your OpenAI organization ID, which is also provided when you sign up.
g = UniversalCompletion(api_key="sk-SOME_CHARS_HERE", org_id="org-SOME_CHARS_HERE")

# This connects to the OpenAI API using the provided API key and organization ID.
g.connect()

# Now we specify which language model we want to use.
# Here, 'gpt-3.5-turbo' is specified, which is a version of the GPT-3 model.
g.set_model('gpt-3.5-turbo')

# This method is used to verify the model type.
# It returns a string that represents the endpoint for the current model in use.
g.validate_model_type(g.model)  # Returns: 'v1/chat/completions'

# We send a request to the language model here.
# The prompt is a question, given in a format that the model understands.
# The model responds with a completion - an extension of the prompt based on what it has learned during training.
# The returned object is a representation of the response from the model.
g.fetch_response(prompt=[{"role": "user", "content": "What is an abstraction?"}])
# Returns a JSON response with the assistant's message.

gptty's People

Contributors

hyp-er avatar signebedi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

gptty's Issues

Using arrow keys to edit question prints escape characters

When trying to edit my query, you can't use your arrow keys to traverse the prompt - one extremely necessary feature when working with the TTY. It gets especially annoying when you have to delete half your prompt to fix one simple spelling mistake, which can yield incorrect responses.

[runtime] add bool config log_reponses

We should let users decide whether to log responses, by setting a bool config log_responses that default to true. Then, if it assesses false, we don't create or write to the output file. We also do not maintain the pandas data frame, and we disable the :l[og] metacommand option within the chat.

[docs] add some guidelines for bash scripting

There area couple ways to script the CLI using bash. For example, you can automate the process of sending multiple questions to the gptty query command using a bash script. This can be particularly useful if you have a list of questions stored in a file, and you want to process them all at once. For example, let's say you have a file questions.txt with each question on a new line, like below.

What are the key differences between machine learning, deep learning, and artificial intelligence?
How do I choose the best programming language for a specific project or task?
Can you recommend some best practices for code optimization and performance improvement?
What are the essential principles of good software design and architecture?
How do I get started with natural language processing and text analysis in Python?
What are some popular Python libraries or frameworks for building web applications?
Can you suggest some resources to learn about data visualization and its implementation in Python?
What are some important concepts in cybersecurity, and how can I apply them to my projects?
How do I ensure that my machine learning models are fair, ethical, and unbiased?
Can you recommend strategies for staying up-to-date with the latest trends and advancements in technology and programming?

You can send each question from the questions.txt file to the gptty query command using the following bash one-liner:

xargs -d '\n' -I {} gptty query --question "{}" < questions.txt

[runtime] add a check for internet connectivity and return error if no connection

See eg. https://stackoverflow.com/a/62115290/13301284.

If the user runs the chatroom or query, but there is no internet connection, return an error message in main.py.

import socket
from contextlib import closing

def has_internet_connection(host="google.com", port=443, timeout=3):
    """Check if the system has a valid internet connection.

    Args:
        host (str, optional): A well-known website to test the connection. Default is 'www.google.com'.
        port (int, optional): The port number to use for the connection. Default is 80.
        timeout (int, optional): The time in seconds to wait for a response before giving up. Default is 3.

    Returns:
        bool: True if the system has an internet connection, False otherwise.
    """
    try:
        with closing(socket.create_connection((host, port), timeout=timeout)):
            return True
    except OSError:
        return False



....



  # Print the text in cyan
  click.echo(f"{CYAN}{title}\nWelcome to gptty (v.{__version__}), a ChatGPT wrapper in your TTY.\nType :help in the chat interface if you need help getting started.{' Verbose / Debug mode is on.' if verbose else ''}{RESET}\n")
  
  if not os.path.exists(config_path):
      click.echo(f"{RED}FAILED to access app config file at {config_path}. Are you sure this is a valid config file? Run `gptty chat --help` for more information. You can get a sample config at <https://github.com/signebedi/gptty/blob/master/assets/gptty.ini.example>.")
      return

  # load the app configs
  configs = get_config_data(config_file=config_path)
  
  if not has_internet_connection(config('verify_internet_endpoint')):
      click.echo(f"{RED}FAILED to verify connection at {config('verify_internet_endpoint')}. Are you sure you are connected to the internet?")
      return

  # create the output file if it doesn't exist
  with open (configs['output_file'], 'a'): pass

[context] allow positional context passing

Allow user to pass positional information as a tag to :context and to questions, see:

...
:context[a:b] - show context history with optional index ranges a and b *under development*
...
To pass context positionally, prepend questions with tags like `[a:b] what is the meaning of life`.

We will need to modify the context.py logic to stringify the index range.

[model] add support for chatcompletion models

add support for chatcompletion models
Currently, we only support completion models. Subsequently, we will need to find a way to support chat completion too.

References

  1. chat completion source code python https://github.com/openai/openai-python/blob/main/openai/api_resources/chat_completion.py#L8
  2. chat completion create https://platform.openai.com/docs/api-reference/chat/create
  3. chat completions general introduction https://platform.openai.com/docs/guides/chat
  4. list of models https://platform.openai.com/docs/models/overview

Originally posted by @signebedi in #30 (comment)

[model] add model validation

We should validate that the model passed is an actual model.

def get_available_models():
    response = openai.Model.list()
    return [model.id for model in response['data']]

def is_valid_model(model_name):
    available_models = get_available_models()
    return model_name in available_models

def validate_model_type(model_name):
    if ('davinci' in model_name or 'curie' in model_name) and is_valid_model(model_name):
        return 'completion'
    elif 'gpt' in model_name and is_valid_model(model_name):
        return 'chat completion'
    else:
        raise Exception()

....


    # Set the parameters for the OpenAI completion API
    model_engine = configs['model'].rstrip('\n')

    try:
        model_type = validate_model_type(model_engine)
    except:
        click.echo(f"{RED}FAILED to validate the model name '{model_engine}'. Are you sure this is a valid OpenAI model? Check the available models at <https://platform.openai.com/docs/models/overview> and try again.{RESET}")
        return

[config] add support for a `gptty.conf` file

This should include things like:

  • API_KEY: your OpenAI API key
  • GPT_VERSION: [future] pivot between GPT 3 and 4
  • YOUR_NAME: set this to Joe, and questions will be tagged as such
  • GPT_NAME: set this to Bob, and responses will be tagged as such
  • OUTPUT_FILE: path to file to write output to

[runtime] add a `--quiet` tag for `query` subcommand

Sometimes users may not want the response printed to stdout but instead just want query responses written to the output_file designated in the config. In these cases, we should allow them to add a --quiet tag.

[runtime] add support for question chains / queues

Users may wish to pass a set of pre-defined questions with the same context (or, maybe even with different contexts) that they can have chat gpt process while they step away from their workstations.

We should add support for a question queue (maybe a file?), or conversely add support for passing multiple questions at runtime gptty run --question "Question 1 here" --question "Question 2 here" --tags 'Your-tag-here'.

A spool / queue file invites the creation of daemonized process to monitor and process that queue....

[context] add support for `--additional_context` option for `query` subcommand

We should add an --additional_context option to the query subcommand that expects either a string or file path, which it will bolt onto the request string (where? at the start? at the end?).

This allows users to extend their questions with details that might not typically be formatted like a question, like stack traces with line breaks, etc.

We should then write some logic to structure this context (removing line breaks, etc) and applying length limits and/or keyword tokenization to it before bolting it onto the request string.

[context] allow keyword-only context using RAKE to minimize API usage fees

OpenAI charges by eg. the number of tokens, meaning that passing a full 5000 words of context will become very costly (see eg. https://platform.openai.com/docs/api-reference/completions/create, where they mention fees for max_token_length). We can probably manage this by adding a Rapid Automatic Keyword Extraction (RAKE) algorithm to pull out the keywords in the context.

from rake_nltk import Rake

# Uses stopwords for english from NLTK, and all puntuation characters by
# default
r = Rake()

# Extraction given the text.
r.extract_keywords_from_text(<text to process>)

We can configure this as an app config.

[main]
context_keywords_only=1

[bug] openai.error.InvalidRequestError: This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?

Using gpt-3.5-turbo model:

[question] tell me three interesting world capital cities 
.....    Traceback (most recent call last):
  File "/home/sig/Code/gptty/venv/bin/gptty", line 11, in <module>
    load_entry_point('gptty', 'console_scripts', 'gptty')()
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/sig/Code/gptty/gptty/__main__.py", line 77, in chat
    asyncio.run(chat_async_wrapper(config_path))
  File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/sig/Code/gptty/gptty/__main__.py", line 107, in chat_async_wrapper
    await create_chat_room(configs=configs, config_path=config_path)
  File "/home/sig/Code/gptty/gptty/gptty.py", line 137, in create_chat_room
    response = await response_task
  File "/home/sig/Code/gptty/gptty/gptty.py", line 43, in fetch_response
    return await openai.Completion.acreate(
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/openai/api_resources/completion.py", line 45, in acreate
    return await super().acreate(*args, **kwargs)
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/openai/api_resources/abstract/engine_api_resource.py", line 217, in acreate
    response, _, api_key = await requestor.arequest(
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/openai/api_requestor.py", line 310, in arequest
    resp, got_stream = await self._interpret_async_response(result, stream)
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/openai/api_requestor.py", line 645, in _interpret_async_response
    self._interpret_response_line(
  File "/home/sig/Code/gptty/venv/lib/python3.8/site-packages/openai/api_requestor.py", line 682, in _interpret_response_line
    raise self.handle_error_response(
openai.error.InvalidRequestError: This is a chat model and not supported in the v1/completions endpoint. Did you mean to use v1/chat/completions?

https://stackoverflow.com/questions/75774873/openai-chatgpt-gpt-3-5-api-error-this-is-a-chat-model-and-not-supported-in-t

[runtime] add response formatting options

Suggestion via github:

Can you fix your program so that it preserves new line breaks? The output I'm seeing throws away GPT output formatting and puts all of the response text on one line. Paragraph breaks are lost, list item breaks are lost, etc...
Perhaps make this one of your config options,
new_lines: preserve | discard

Response formatting could definitely be better, and the best solution would be to give users the ability to select the formatting options.

What I've struggled to do is find the hook to accomplish this within the openai python API. We are using the Completion class of the openai python API. [1] Specifically, we are using the acreate method, which is an async wrapper for the create method. [2] This does not seem to retain the formatting when it provides a response. That said, I'm still delving into the API and might still find something in there that would make this easier. At the end of the day, nothing is stopping us from writing our own application logic to apply formatting onto a plaintext response, but that seems ... especially inelegant if the openai API provides that functionality.

Reference
[1] https://github.com/openai/openai-python/blob/main/openai/api_resources/completion.py#L9
[2] https://platform.openai.com/docs/api-reference/completions/create

Deploy v.0.2.1

Add v.0.2.1 with the modifications we've made to existing functionality, like log subcommand and improved user documentation.

Project Roadmap

[runtime] add help commands

Using backslash commands similar to in the postgres runtime, we should give users the ability for meta functionality like quitting, getting add'l information, etc.

[runtime] gracefully handle keyboard interrupts in `chat`

When we want to re-write our queries in chat, we should be able to use keyboard interrupts without breaking the program. Something like:

        # Get user input
        try:
            i = await session.prompt_async(ANSI(f"{CYAN}> {RESET}"))
            # i = await ainput(f"{CYAN}> ")
            tag,question = get_tag_from_text(i)
            prompt_length = len(question)

        # handle keyboard interrupt
        except KeyboardInterrupt:
            i = None

Install with pypi

We should add a pip installation hook by adding a setup.py and manfest file.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.