Compare commits

..

No commits in common. "3f8cb47a2a20dac762a3de2709c376992259278b" and "cad8d7ac7152a743fd522855f3ee8049cfc6a125" have entirely different histories.

3 changed files with 96 additions and 125 deletions

130
README.md
View File

@ -46,80 +46,79 @@ cmm [global options] command [command options]
The `question` command is used to ask, create, and process questions. The `question` command is used to ask, create, and process questions.
```bash ```bash
cmm question [-t OTAGS]... [-k ATAGS]... [-x XTAGS]... [-o OUTTAGS]... [-A AI_ID] [-M MODEL] [-n NUM] [-m MAX] [-T TEMP] (-a QUESTION | -c QUESTION | -r [MESSAGE ...] | -p [MESSAGE ...]) [-O] [-s FILE]... [-S FILE]... cmm question [-t OTAGS]... [-k ATAGS]... [-x XTAGS]... [-o OUTTAGS]... [-A AI] [-M MODEL] [-n NUM] [-m MAX] [-T TEMP] (-a ASK | -c CREATE | -r REPEAT | -p PROCESS) [-O] [-s SOURCE]... [-S SOURCE]...
``` ```
* `-t, --or-tags OTAGS`: List of tags (one must match) * `-t, --or-tags OTAGS` : List of tags (one must match)
* `-k, --and-tags ATAGS`: List of tags (all must match) * `-k, --and-tags ATAGS` : List of tags (all must match)
* `-x, --exclude-tags XTAGS`: List of tags to exclude * `-x, --exclude-tags XTAGS` : List of tags to exclude
* `-o, --output-tags OUTTAGS`: List of output tags (default: use input tags) * `-o, --output-tags OUTTAGS` : List of output tags (default: use input tags)
* `-A, --AI AI_ID`: AI ID to use * `-A, --AI AI` : AI ID to use
* `-M, --model MODEL`: Model to use * `-M, --model MODEL` : Model to use
* `-n, --num-answers NUM`: Number of answers to request * `-n, --num-answers NUM` : Number of answers to request
* `-m, --max-tokens MAX`: Max. number of tokens * `-m, --max-tokens MAX` : Max. number of tokens
* `-T, --temperature TEMP`: Temperature value * `-T, --temperature TEMP` : Temperature value
* `-a, --ask QUESTION`: Ask a question * `-a, --ask ASK` : Ask a question
* `-c, --create QUESTION`: Create a question * `-c, --create CREATE` : Create a question
* `-r, --repeat [MESSAGE ...]`: Repeat a question * `-r, --repeat REPEAT` : Repeat a question
* `-p, --process [MESSAGE ...]`: Process existing questions * `-p, --process PROCESS` : Process existing questions
* `-O, --overwrite`: Overwrite existing messages when repeating them * `-O, --overwrite` : Overwrite existing messages when repeating them
* `-s, --source-text FILE`: Add content of a file to the query * `-s, --source-text SOURCE` : Add content of a file to the query
* `-S, --source-code FILE`: Add source code file content to the chat history * `-S, --source-code SOURCE` : Add source code file content to the chat history
#### Hist #### Hist
The `hist` command is used to print the chat history. The `hist` command is used to print the chat history.
```bash ```bash
cmm hist [-t OTAGS]... [-k ATAGS]... [-x XTAGS]... [-w] [-W] [-S] [-A SUBSTRING] [-Q SUBSTRING] cmm hist [-t OTAGS]... [-k ATAGS]... [-x XTAGS]... [-w] [-W] [-S] [-A ANSWER] [-Q QUESTION]
``` ```
* `-t, --or-tags OTAGS`: List of tags (one must match) * `-t, --or-tags OTAGS` : List of tags (one must match)
* `-k, --and-tags ATAGS`: List of tags (all must match) * `-k, --and-tags ATAGS` : List of tags (all must match)
* `-x, --exclude-tags XTAGS`: List of tags to exclude * `-x, --exclude-tags XTAGS` : List of tags to exclude
* `-w, --with-tags`: Print chat history with tags * `-w, --with-tags` : Print chat history with tags
* `-W, --with-files`: Print chat history with filenames * `-W, --with-files` : Print chat history with filenames
* `-S, --source-code-only`: Only print embedded source code * `-S, --source-code-only` : Print only source code
* `-A, --answer SUBSTRING`: Search for answer substring * `-A, --answer ANSWER` : Search for answer substring
* `-Q, --question SUBSTRING`: Search for question substring * `-Q, --question QUESTION` : Search for question substring
#### Tags #### Tags
The `tags` command is used to manage tags. The `tags` command is used to manage tags.
```bash ```bash
cmm tags (-l | -p PREFIX | -c SUBSTRING) cmm tags (-l | -p PREFIX | -c CONTENT)
``` ```
* `-l, --list`: List all tags and their frequency * `-l, --list` : List all tags and their frequency
* `-p, --prefix PREFIX`: Filter tags by prefix * `-p, --prefix PREFIX` : Filter tags by prefix
* `-c, --contain SUBSTRING`: Filter tags by contained substring * `-c, --contain CONTENT` : Filter tags by contained substring
#### Config #### Config
The `config` command is used to manage the configuration. The `config` command is used to manage the configuration.
```bash ```bash
cmm config (-l | -m | -c FILE) cmm config (-l | -m | -c CREATE)
``` ```
* `-l, --list-models`: List all available models * `-l, --list-models` : List all available models
* `-m, --print-model`: Print the currently configured model * `-m, --print-model` : Print the currently configured model
* `-c, --create FILE`: Create config with default settings in the given file * `-c, --create CREATE` : Create config with default settings in the given file
#### Print #### Print
The `print` command is used to print message files. The `print` command is used to print message files.
```bash ```bash
cmm print (-f FILE | -l) [-q | -a | -S] cmm print -f FILE [-q | -a | -S]
``` ```
* `-f, --file FILE`: Print given file * `-f, --file FILE` : File to print
* `-l, --latest`: Print latest message * `-q, --question` : Print only question
* `-q, --question`: Only print the question * `-a, --answer` : Print only answer
* `-a, --answer`: Only print the answer * `-S, --only-source-code` : Print only source code
* `-S, --only-source-code`: Only print embedded source code
### Examples ### Examples
@ -161,27 +160,18 @@ cmm print -f example.yaml
## Configuration ## Configuration
The default configuration filename is `.config.yaml` (it is searched in the current working directory). The configuration file (`.config.yaml`) should contain the following fields:
Use the command `cmm config --create <FILENAME>` to create a default configuration:
``` - `openai`:
cache: . - `api_key`: Your OpenAI API key.
db: ./db/ - `model`: The name of the OpenAI model to use (e.g. "text-davinci-002").
ais: - `temperature`: The temperature value for the model.
myopenai: - `max_tokens`: The maximum number of tokens for the model.
name: openai - `top_p`: The top P value for the model.
model: gpt-3.5-turbo-16k - `frequency_penalty`: The frequency penalty value.
api_key: 0123456789 - `presence_penalty`: The presence penalty value.
temperature: 1.0 - `system`: The system message used to set the behavior of the AI.
max_tokens: 4000 - `db`: The directory where the question-answer pairs are stored in YAML files.
top_p: 1.0
frequency_penalty: 0.0
presence_penalty: 0.0
system: You are an assistant
```
Each AI has its own section and the name of that section is called the 'AI ID' (in the example above it is `myopenai`).
The AI ID can be any string, as long as it's unique within the `ais` section. The AI ID is used for all commands that support the `AI` parameter and it's also stored within each message file.
## Autocompletion ## Autocompletion
@ -196,33 +186,33 @@ After adding this line, restart your shell or run `source <your-shell-config-fil
## Contributing ## Contributing
### Enable commit hooks ### Enable commit hooks
```bash ```
pip install pre-commit pip install pre-commit
pre-commit install pre-commit install
``` ```
### Execute tests before opening a PR ### Execute tests before opening a PR
```bash ```
pytest pytest
``` ```
### Consider using `pyenv` / `pyenv-virtualenv` ### Consider using `pyenv` / `pyenv-virtualenv`
Short installation instructions: Short installation instructions:
* install `pyenv`: * install `pyenv`:
```bash ```
cd ~ cd ~
git clone https://github.com/pyenv/pyenv .pyenv git clone https://github.com/pyenv/pyenv .pyenv
cd ~/.pyenv && src/configure && make -C src cd ~/.pyenv && src/configure && make -C src
``` ```
* make sure that `~/.pyenv/shims` and `~/.pyenv/bin` are the first entries in your `PATH`, e.g., by setting it in `~/.bashrc` * make sure that `~/.pyenv/shims` and `~/.pyenv/bin` are the first entries in your `PATH`, e. g. by setting it in `~/.bashrc`
* add the following to your `~/.bashrc` (after setting `PATH`): `eval "$(pyenv init -)"` * add the following to your `~/.bashrc` (after setting `PATH`): `eval "$(pyenv init -)"`
* create a new terminal or source the changes (e.g., `source ~/.bashrc`) * create a new terminal or source the changes (e. g. `source ~/.bashrc`)
* install `virtualenv` * install `virtualenv`
```bash ```
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
``` ```
* add the following to your `~/.bashrc` (after the commands above): `eval "$(pyenv virtualenv-init -)` * add the following to your `~/.bashrc` (after the commands above): `eval "$(pyenv virtualenv-init -)`
* create a new terminal or source the changes (e.g., `source ~/.bashrc`) * create a new terminal or source the changes (e. g. `source ~/.bashrc`)
* go back to the `ChatMasterMind` repo and create a virtual environment with the latest `Python`, e.g., `3.11.4`: * go back to the `ChatMasterMind` repo and create a virtual environment with the latest `Python`, e. g. `3.11.4`:
```bash ```
cd <CMM_REPO_PATH> cd <CMM_REPO_PATH>
pyenv install 3.11.4 pyenv install 3.11.4
pyenv virtualenv 3.11.4 py311 pyenv virtualenv 3.11.4 py311
@ -233,3 +223,5 @@ pyenv activate py311
## License ## License
This project is licensed under the terms of the WTFPL License. This project is licensed under the terms of the WTFPL License.

View File

@ -3,43 +3,25 @@ import argparse
from pathlib import Path from pathlib import Path
from ..configuration import Config from ..configuration import Config
from ..message import Message, MessageError from ..message import Message, MessageError
from ..chat import ChatDB
def print_message(message: Message, args: argparse.Namespace) -> None:
"""
Print given message according to give arguments.
"""
if args.question:
print(message.question)
elif args.answer:
print(message.answer)
elif message.answer and args.only_source_code:
for code in message.answer.source_code():
print(code)
else:
print(message.to_str())
def print_cmd(args: argparse.Namespace, config: Config) -> None: def print_cmd(args: argparse.Namespace, config: Config) -> None:
""" """
Handler for the 'print' command. Handler for the 'print' command.
""" """
# print given file fname = Path(args.file)
if args.file is not None: try:
fname = Path(args.file) message = Message.from_file(fname)
try: if message:
message = Message.from_file(fname) if args.question:
if message: print(message.question)
print_message(message, args) elif args.answer:
except MessageError: print(message.answer)
print(f"File is not a valid message: {args.file}") elif message.answer and args.only_source_code:
sys.exit(1) for code in message.answer.source_code():
# print latest message print(code)
elif args.latest: else:
chat = ChatDB.from_dir(Path(config.cache), Path(config.db)) print(message.to_str())
latest = chat.msg_latest(loc='disk') except MessageError:
if not latest: print(f"File is not a valid message: {args.file}")
print("No message found!") sys.exit(1)
sys.exit(1)
print_message(latest, args)

View File

@ -44,13 +44,13 @@ def create_parser() -> argparse.ArgumentParser:
help='List of tags to exclude', metavar='XTAGS') help='List of tags to exclude', metavar='XTAGS')
etag_arg.completer = tags_completer # type: ignore etag_arg.completer = tags_completer # type: ignore
otag_arg = tag_parser.add_argument('-o', '--output-tags', nargs='+', otag_arg = tag_parser.add_argument('-o', '--output-tags', nargs='+',
help='List of output tags (default: use input tags)', metavar='OUTAGS') help='List of output tags (default: use input tags)', metavar='OUTTAGS')
otag_arg.completer = tags_completer # type: ignore otag_arg.completer = tags_completer # type: ignore
# a parent parser for all commands that support AI configuration # a parent parser for all commands that support AI configuration
ai_parser = argparse.ArgumentParser(add_help=False) ai_parser = argparse.ArgumentParser(add_help=False)
ai_parser.add_argument('-A', '--AI', help='AI ID to use', metavar='AI_ID') ai_parser.add_argument('-A', '--AI', help='AI ID to use')
ai_parser.add_argument('-M', '--model', help='Model to use', metavar='MODEL') ai_parser.add_argument('-M', '--model', help='Model to use')
ai_parser.add_argument('-n', '--num-answers', help='Number of answers to request', type=int, default=1) ai_parser.add_argument('-n', '--num-answers', help='Number of answers to request', type=int, default=1)
ai_parser.add_argument('-m', '--max-tokens', help='Max. nr. of tokens', type=int) ai_parser.add_argument('-m', '--max-tokens', help='Max. nr. of tokens', type=int)
ai_parser.add_argument('-T', '--temperature', help='Temperature value', type=float) ai_parser.add_argument('-T', '--temperature', help='Temperature value', type=float)
@ -61,15 +61,14 @@ def create_parser() -> argparse.ArgumentParser:
aliases=['q']) aliases=['q'])
question_cmd_parser.set_defaults(func=question_cmd) question_cmd_parser.set_defaults(func=question_cmd)
question_group = question_cmd_parser.add_mutually_exclusive_group(required=True) question_group = question_cmd_parser.add_mutually_exclusive_group(required=True)
question_group.add_argument('-a', '--ask', nargs='+', help='Ask a question', metavar='QUESTION') question_group.add_argument('-a', '--ask', nargs='+', help='Ask a question')
question_group.add_argument('-c', '--create', nargs='+', help='Create a question', metavar='QUESTION') question_group.add_argument('-c', '--create', nargs='+', help='Create a question')
question_group.add_argument('-r', '--repeat', nargs='*', help='Repeat a question', metavar='MESSAGE') question_group.add_argument('-r', '--repeat', nargs='*', help='Repeat a question')
question_group.add_argument('-p', '--process', nargs='*', help='Process existing questions', metavar='MESSAGE') question_group.add_argument('-p', '--process', nargs='*', help='Process existing questions')
question_cmd_parser.add_argument('-O', '--overwrite', help='Overwrite existing messages when repeating them', question_cmd_parser.add_argument('-O', '--overwrite', help='Overwrite existing messages when repeating them',
action='store_true') action='store_true')
question_cmd_parser.add_argument('-s', '--source-text', nargs='+', help='Add content of a file to the query', metavar='FILE') question_cmd_parser.add_argument('-s', '--source-text', nargs='+', help='Add content of a file to the query')
question_cmd_parser.add_argument('-S', '--source-code', nargs='+', help='Add source code file content to the chat history', question_cmd_parser.add_argument('-S', '--source-code', nargs='+', help='Add source code file content to the chat history')
metavar='FILE')
# 'hist' command parser # 'hist' command parser
hist_cmd_parser = cmdparser.add_parser('hist', parents=[tag_parser], hist_cmd_parser = cmdparser.add_parser('hist', parents=[tag_parser],
@ -80,10 +79,10 @@ def create_parser() -> argparse.ArgumentParser:
action='store_true') action='store_true')
hist_cmd_parser.add_argument('-W', '--with-files', help="Print chat history with filenames.", hist_cmd_parser.add_argument('-W', '--with-files', help="Print chat history with filenames.",
action='store_true') action='store_true')
hist_cmd_parser.add_argument('-S', '--source-code-only', help='Only print embedded source code', hist_cmd_parser.add_argument('-S', '--source-code-only', help='Print only source code',
action='store_true') action='store_true')
hist_cmd_parser.add_argument('-A', '--answer', help='Search for answer substring', metavar='SUBSTRING') hist_cmd_parser.add_argument('-A', '--answer', help='Search for answer substring')
hist_cmd_parser.add_argument('-Q', '--question', help='Search for question substring', metavar='SUBSTRING') hist_cmd_parser.add_argument('-Q', '--question', help='Search for question substring')
# 'tags' command parser # 'tags' command parser
tags_cmd_parser = cmdparser.add_parser('tags', tags_cmd_parser = cmdparser.add_parser('tags',
@ -93,8 +92,8 @@ def create_parser() -> argparse.ArgumentParser:
tags_group = tags_cmd_parser.add_mutually_exclusive_group(required=True) tags_group = tags_cmd_parser.add_mutually_exclusive_group(required=True)
tags_group.add_argument('-l', '--list', help="List all tags and their frequency", tags_group.add_argument('-l', '--list', help="List all tags and their frequency",
action='store_true') action='store_true')
tags_cmd_parser.add_argument('-p', '--prefix', help="Filter tags by prefix", metavar='PREFIX') tags_cmd_parser.add_argument('-p', '--prefix', help="Filter tags by prefix")
tags_cmd_parser.add_argument('-c', '--contain', help="Filter tags by contained substring", metavar='SUBSTRING') tags_cmd_parser.add_argument('-c', '--contain', help="Filter tags by contained substring")
# 'config' command parser # 'config' command parser
config_cmd_parser = cmdparser.add_parser('config', config_cmd_parser = cmdparser.add_parser('config',
@ -107,20 +106,18 @@ def create_parser() -> argparse.ArgumentParser:
action='store_true') action='store_true')
config_group.add_argument('-m', '--print-model', help="Print the currently configured model", config_group.add_argument('-m', '--print-model', help="Print the currently configured model",
action='store_true') action='store_true')
config_group.add_argument('-c', '--create', help="Create config with default settings in the given file", metavar='FILE') config_group.add_argument('-c', '--create', help="Create config with default settings in the given file")
# 'print' command parser # 'print' command parser
print_cmd_parser = cmdparser.add_parser('print', print_cmd_parser = cmdparser.add_parser('print',
help="Print message files.", help="Print message files.",
aliases=['p']) aliases=['p'])
print_cmd_parser.set_defaults(func=print_cmd) print_cmd_parser.set_defaults(func=print_cmd)
print_group = print_cmd_parser.add_mutually_exclusive_group(required=True) print_cmd_parser.add_argument('-f', '--file', help='File to print', required=True)
print_group.add_argument('-f', '--file', help='Print given message file', metavar='FILE')
print_group.add_argument('-l', '--latest', help='Print latest message', action='store_true')
print_cmd_modes = print_cmd_parser.add_mutually_exclusive_group() print_cmd_modes = print_cmd_parser.add_mutually_exclusive_group()
print_cmd_modes.add_argument('-q', '--question', help='Only print the question', action='store_true') print_cmd_modes.add_argument('-q', '--question', help='Print only question', action='store_true')
print_cmd_modes.add_argument('-a', '--answer', help='Only print the answer', action='store_true') print_cmd_modes.add_argument('-a', '--answer', help='Print only answer', action='store_true')
print_cmd_modes.add_argument('-S', '--only-source-code', help='Only print embedded source code', action='store_true') print_cmd_modes.add_argument('-S', '--only-source-code', help='Print only source code', action='store_true')
argcomplete.autocomplete(parser) argcomplete.autocomplete(parser)
return parser return parser