Skip to content

Commit

Permalink
Merge pull request #5 from sbslee/0.6.0-dev
Browse files Browse the repository at this point in the history
0.6.0 dev
  • Loading branch information
sbslee committed Jun 12, 2023
2 parents df705d4 + 2f3a145 commit 4cf1a86
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 37 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 0.6.0 (2023-06-12)
* Enable users to chat with .csv documents.
* Enable users to customize chat settings (e.g. font size and background color).

## 0.5.0 (2023-06-08)
* Enable users to ask follow-up questions when chatting with documents.
* Enable users to customize chatbot parameters by uploading a configuration file.
Expand Down
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,71 @@

[![PyPI](https://badge.fury.io/py/kanu.svg)](https://badge.fury.io/py/kanu)

KANU is a minimalistic Python-based GUI for various chatbots.
Welcome to KANU, a minimalistic Python-based GUI for various chatbots.

## Chatbots
There are currently two chatbots available in KANU:

### ChatGPT
- [ChatGPT](#chatgpt) harnesses the power of ChatGPT, bringing it directly to your local computer
- [DocGPT](#docgpt) allows you to effortlessly interact with your documents and ask questions about them

![Alt Text](https://raw.githubusercontent.com/sbslee/kanu/main/images/chatgpt.gif)
Other features of KANU inclde:

### DocGPT

![Alt Text](https://raw.githubusercontent.com/sbslee/kanu/main/images/docgpt.gif)
- Customize chat settings (e.g. font size and background color)
- Customize chatbot parameters (e.g. prompt, temperature, and chunk size) by directly using the GUI or uploading a configuration file

## Installation

The recommended way is via pip:

```
$ pip install kanu
```

KANU requires a different set of dependencies for each chatbot. You can find the dependencies specific to each chatbot in the [Chatbots](#chatbots) section.

## Running

```
$ kanu
```

<a id="chatbots"></a>
## Chatbots

<a id="chatgpt"></a>
### ChatGPT

![Alt Text](https://raw.githubusercontent.com/sbslee/kanu/main/images/chatgpt.gif)

The following packages are required to run ChatGPT:

```
openai # Required.
```

<a id="docgpt"></a>
### DocGPT

![Alt Text](https://raw.githubusercontent.com/sbslee/kanu/main/images/docgpt.gif)

The following document formats are supported by DocGPT:

- .txt
- .pdf
- .doc and .docx
- .csv

The following packages are required to run DocGPT:

```
langchain # Required.
chromadb # Required.
tiktoken # Required.
pdfminer.six # Optional. Only required for .pdf documents.
unstructured # Optional. Only required for .doc and .docx documents.
tabulate # Optional. Only required for .doc and .docx documents.
```

## Changelog

See the [CHANGELOG.md](https://github.com/sbslee/kanu/blob/main/CHANGELOG.md) file for details.
26 changes: 19 additions & 7 deletions kanu/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,20 @@ def config_chatgpt(self):

def parse_chatgpt_config(self):
config = configparser.ConfigParser()
config.read(filedialog.askopenfilename())
file_path = filedialog.askopenfilename()
if not file_path:
return
config.read(file_path)
self.deploy_agent("ChatGPT", config["USER"]["openai_key"], config["DEFAULT"]["model"], float(config["DEFAULT"]["temperature"]), config["DEFAULT"]["prompt"])

def template_chatgpt_config(self):
file_path = filedialog.asksaveasfilename()
if not file_path:
return
config = configparser.ConfigParser()
config["DEFAULT"] = {"model": "gpt-3.5-turbo", "temperature": "0.5", "prompt": CHATGPT_PROMPT}
config["USER"] = {"openai_key": ""}
with open(filedialog.asksaveasfilename(), "w") as f:
with open(file_path, "w") as f:
config.write(f)

def config_docgpt(self):
Expand Down Expand Up @@ -144,21 +150,27 @@ def config_docgpt(self):
l.grid(row=18, column=0, columnspan=2)
e = tk.Entry(self.container)
e.grid(row=19, column=0, columnspan=2)
b = tk.Button(self.container, text="Submit", command=lambda: self.deploy_agent("DocGPT", e.get(), self.model.get(), self.prompt.get("1.0", "end-1c"), self.temperature.get()))
b = tk.Button(self.container, text="Submit", command=lambda: self.deploy_agent("DocGPT", e.get(), self.model.get(), self.prompt.get("1.0", "end-1c"), self.temperature.get(), 1000, 50))
b.grid(row=20, column=0)
b = tk.Button(self.container, text="Go back", command=lambda: self.homepage())
b.grid(row=20, column=1)

def parse_docgpt_config(self):
config = configparser.ConfigParser()
config.read(filedialog.askopenfilename())
self.deploy_agent("DocGPT", config["USER"]["openai_key"], config["DEFAULT"]["model"], float(config["DEFAULT"]["temperature"]), config["DEFAULT"]["prompt"])
file_path = filedialog.askopenfilename()
if not file_path:
return
config.read(file_path)
self.deploy_agent("DocGPT", config["USER"]["openai_key"], config["DEFAULT"]["model"], float(config["DEFAULT"]["temperature"]), config["DEFAULT"]["prompt"], config["DEFAULT"]["chunk_size"], config["DEFAULT"]["chunk_overlap"])

def template_docgpt_config(self):
file_path = filedialog.asksaveasfilename()
if not file_path:
return
config = configparser.ConfigParser()
config["DEFAULT"] = {"model": "gpt-3.5-turbo", "temperature": "0.5", "prompt": DOCGPT_PROMPT}
config["DEFAULT"] = {"model": "gpt-3.5-turbo", "temperature": "0.5", "prompt": DOCGPT_PROMPT, "chunk_size": 1000, "chunk_overlap": 50}
config["USER"] = {"openai_key": ""}
with open(filedialog.asksaveasfilename(), "w") as f:
with open(file_path, "w") as f:
config.write(f)

def deploy_agent(self, agent, *args, **kwargs):
Expand Down
21 changes: 14 additions & 7 deletions kanu/chatgpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,38 @@

import openai

from .utils import Settings

class ChatGPT:
def __init__(self, kanu, openai_key, model, temperature, prompt):
self.kanu = kanu
self.model = model
self.temperature = temperature
self.prompt = prompt
openai.api_key = openai_key
self.settings = Settings(self)

def run(self):
self.kanu.container.pack_forget()
self.kanu.container = tk.Frame(self.kanu.root)
self.kanu.container.pack()
l = tk.Label(self.kanu.container, text="ChatGPT")
l.grid(row=0, column=0, columnspan=3)
l.grid(row=0, column=0, columnspan=4)
self.session = tk.Text(self.kanu.container, width=70, height=20)
self.session.grid(row=1, column=0, columnspan=3)
e = tk.Entry(self.kanu.container, width=54)
e.grid(row=2, column=0, columnspan=3)
self.session.grid(row=1, column=0, columnspan=4)
self.session.tag_config("user", **self.settings.get_user_kwargs())
self.session.tag_config("bot", **self.settings.get_bot_kwargs())
user_input = tk.Entry(self.kanu.container, width=54)
user_input.grid(row=2, column=0, columnspan=4)
self.messages = []
b = tk.Button(self.kanu.container, text="Send", command=lambda: self.send_message(e))
b = tk.Button(self.kanu.container, text="Send", command=lambda: self.send_message(user_input))
b.grid(row=3, column=0)
b = tk.Button(self.kanu.container, text="Clear", command=lambda: self.clear_session())
b.grid(row=3, column=1)
b = tk.Button(self.kanu.container, text="Go back", command=lambda: self.kanu.config_chatgpt())
b.grid(row=3, column=2)
b = tk.Button(self.kanu.container, text="Settings", command=lambda: self.settings.page())
b.grid(row=3, column=3)

def send_message(self, entry):
if not self.messages:
Expand All @@ -39,8 +46,8 @@ def send_message(self, entry):
)
response = bot_response["choices"][0]["message"]["content"]
self.messages += [{"role": "assistant", "content": response}]
self.session.insert(tk.END, "You: " + entry.get() + "\n")
self.session.insert(tk.END, f"Bot: " + response + "\n")
self.session.insert(tk.END, "You: " + entry.get() + "\n", "user")
self.session.insert(tk.END, f"Bot: " + response + "\n", "bot")
entry.delete(0, tk.END)

def clear_session(self):
Expand Down
39 changes: 24 additions & 15 deletions kanu/docgpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,30 @@
from langchain.document_loaders import (
TextLoader,
PDFMinerLoader,
UnstructuredWordDocumentLoader
UnstructuredWordDocumentLoader,
CSVLoader,
)

from .utils import Tooltip
from .utils import Tooltip, Settings

DOCUMENT_LOADERS = {
".txt": (TextLoader, {"encoding": "utf8"}),
".pdf": (PDFMinerLoader, {}),
".doc": (UnstructuredWordDocumentLoader, {}),
".docx": (UnstructuredWordDocumentLoader, {}),
".csv": (CSVLoader, {}),
}

class DocGPT:
def __init__(self, kanu, openai_key, model, temperature, prompt):
def __init__(self, kanu, openai_key, model, temperature, prompt, default_chunk_size, default_chunk_overlap):
self.kanu = kanu
self.model = model
self.temperature = temperature
self.prompt = prompt
self.default_chunk_size = default_chunk_size
self.default_chunk_overlap = default_chunk_overlap
os.environ["OPENAI_API_KEY"] = openai_key
self.settings = Settings(self)

def run(self):
self.kanu.container.pack_forget()
Expand Down Expand Up @@ -62,13 +67,13 @@ def run(self):
l = tk.Label(self.kanu.container, text="Chunk size ⓘ:")
Tooltip(l, "The maximum number of characters in each chunk.")
l.grid(row=5, column=0)
self.chunk_size = tk.IntVar(self.kanu.container, value=1000)
self.chunk_size = tk.IntVar(self.kanu.container, value=self.default_chunk_size)
e = tk.Entry(self.kanu.container, textvariable=self.chunk_size)
e.grid(row=5, column=1, columnspan=2)
l = tk.Label(self.kanu.container, text="Chunk overlap ⓘ:")
Tooltip(l, "The number of overlapping characters between adjacent chunks.")
l.grid(row=6, column=0)
self.chunk_overlap = tk.IntVar(self.kanu.container, value=50)
self.chunk_overlap = tk.IntVar(self.kanu.container, value=self.default_chunk_overlap)
e = tk.Entry(self.kanu.container, textvariable=self.chunk_overlap)
e.grid(row=6, column=1, columnspan=2)
self.option1_button = tk.Button(self.kanu.container, text="Go with Option 1", command=self.go_with_option1)
Expand Down Expand Up @@ -101,22 +106,26 @@ def query(self):
self.kanu.container = tk.Frame(self.kanu.root)
self.kanu.container.pack()
l = tk.Label(self.kanu.container, text="DocGPT")
l.grid(row=0, column=0, columnspan=3)
l.grid(row=0, column=0, columnspan=4)
self.session = tk.Text(self.kanu.container, width=70, height=20)
self.session.grid(row=1, column=0, columnspan=3)
e = tk.Entry(self.kanu.container, width=54)
e.grid(row=2, column=0, columnspan=3)
b = tk.Button(self.kanu.container, text="Send", command=lambda: self.send_message(e))
self.session.grid(row=1, column=0, columnspan=4)
self.session.tag_config("user", **self.settings.get_user_kwargs())
self.session.tag_config("bot", **self.settings.get_bot_kwargs())
user_input = tk.Entry(self.kanu.container, width=54)
user_input.grid(row=2, column=0, columnspan=4)
b = tk.Button(self.kanu.container, text="Send", command=lambda: self.send_message(user_input))
b.grid(row=3, column=0)
b = tk.Button(self.kanu.container, text="Clear", command=lambda: self.clear_session())
b.grid(row=3, column=1)
b = tk.Button(self.kanu.container, text="Go back", command=lambda: self.run())
b.grid(row=3, column=2)
b = tk.Button(self.kanu.container, text="Settings", command=lambda: self.settings.page())
b.grid(row=3, column=3)

def send_message(self, entry):
self.session.insert(tk.END, "You: " + entry.get() + "\n")
self.session.insert(tk.END, "You: " + entry.get() + "\n", "user")
response = self.qa(entry.get())["answer"]
self.session.insert(tk.END, "Bot: " + response + "\n")
self.session.insert(tk.END, "Bot: " + response + "\n", "bot")
entry.delete(0, tk.END)

def go_with_option1(self):
Expand All @@ -129,12 +138,12 @@ def go_with_option1(self):
continue
loader_class, loader_kwargs = DOCUMENT_LOADERS[file_ext]
loader = loader_class(file_path, **loader_kwargs)
document = loader.load()[0]
documents.append(document)
document = loader.load()
documents.extend(document)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=self.chunk_size.get(), chunk_overlap=self.chunk_overlap.get())
texts = text_splitter.split_documents(documents)
db = Chroma.from_documents(texts, OpenAIEmbeddings(), persist_directory=self.database_directory)
db.add_documents(texts)
db.add_documents(texts)
db.persist()
db = None
self.query()
Expand Down
Loading

0 comments on commit 4cf1a86

Please sign in to comment.