Skip to content

Commit 9b5e227

Browse files
committed
9 Sep 2023
Installer default to "GPU", "borrowed" accelerate snippet from Oobabooga. Should load and run a little faster now.
1 parent 11dfdcf commit 9b5e227

File tree

12 files changed

+144
-98
lines changed

12 files changed

+144
-98
lines changed

ai chatbot/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ https://code-boxx.com/core-boxx-ai-chatbot/
55
* [Core Boxx](https://github.com/code-boxx/Core-Boxx-PHP-Framework/tree/main/core)
66
* [Python](https://www.python.org/) At the time of writing, 3.9~3.10 works fine.
77
* [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/downloads/?q=build+tools)
8+
* A decent graphics card. Even if you tweak and run with CPU-only, it will be painfully slow...
89

910
## INSTALLATION
1011
* Copy/unzip this module into your existing Core Boxx project folder.
1112
* Put documents you want the AI to "learn" into `chatbot/docs`, accepted file types - `csv pdf txt epub html md odt doc docx ppt pptx`.
12-
* Run setup - *BE WARNED, SEVERAL GIGABYTES WORTH OF DOWNLOAD!*
13-
* Windows - Run `0-setup.bat` for "CPU only", or `0-setup.bat GPU` if you have an Nvidia graphics card.
14-
* Linux - Run `0-setup.sh` for "CPU only", or `0-setup.sh GPU` if you have an Nvidia graphics card.
13+
* Run `0-setup.bat` (Windows) `0-setup.sh` (Linux) - *BE WARNED, SEVERAL GIGABYTES WORTH OF DOWNLOAD!*
1514
* Access `http://your-site.com/ai/` for the demo.
1615

1716
## NOTES

ai chatbot/chatbot/0-setup.bat

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
php 0-setup.php %1
1+
php 0-setup.php
22
virtualenv venv
33
call venv\Scripts\activate
44
pip install langchain transformers optimum auto-gptq chromadb sentence_transformers Flask pyjwt
5-
if "%1"=="GPU" (
6-
pip install torch torchvision torchaudio --force-reinstall --index-url https://download.pytorch.org/whl/cu117
7-
) else (
5+
if "%1"=="CPU" (
86
pip install torch torchvision torchaudio --force-reinstall
7+
) else (
8+
pip install torch torchvision torchaudio --force-reinstall --index-url https://download.pytorch.org/whl/cu117
99
)
10-
python create.py
11-
python bot.py
10+
python b_create.py
11+
python d_bot.py

ai chatbot/chatbot/0-setup.php

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
<?php
2-
// (A) KOA SUTATO
3-
require dirname(__DIR__) . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "CORE-Go.php";
2+
// (A) RODO KOA KONFIGU
3+
require dirname(__DIR__) . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "CORE-Config.php";
44

55
// (B) NEW CHATBOT PATH
66
define("PATH_CHATBOT", PATH_BASE . "chatbot" . DIRECTORY_SEPARATOR);
77

8-
// (C) BACKUP CHATBOT/SETTINGS.PY
9-
if (!copy(PATH_CHATBOT . "settings.py", PATH_CHATBOT . "settings.old")) {
10-
exit("Failed to backup settings file - " . PATH_CHATBOT . "settings.old");
8+
// (C) BACKUP CHATBOT/A_SETTINGS.PY
9+
if (!copy(PATH_CHATBOT . "a_settings.py", PATH_CHATBOT . "a_settings.old")) {
10+
exit("Failed to backup settings file - " . PATH_CHATBOT . "a_settings.old");
1111
}
1212

13-
// (D) COPY HOST SETTINGS FROM CORE-CONFIG.PHP TO SETTINGS.PY
13+
// (D) COPY SETTINGS FROM CORE-CONFIG.PHP TO A_SETTINGS.PY
1414
$replace = [
15-
"model_name" => isset($argv[1]) && $argv[1]=="GPU"
16-
? '"TheBloke/Wizard-Vicuna-7B-Uncensored-GPTQ"'
17-
: '"TheBloke/Wizard-Vicuna-7B-Uncensored-GGML"',
1815
"http_allow" => "[\"http://".HOST_NAME."\", \"https://".HOST_NAME."\"]",
1916
"http_host" => "\"".HOST_NAME."\"",
2017
"jwt_algo" => "\"".JWT_ALGO."\"",
2118
"jwt_secret" => "\"".JWT_SECRET."\""
2219
];
23-
$cfg = file(PATH_CHATBOT . "settings.py") or exit("Cannot read". PATH_CHATBOT ."settings.py");
20+
$cfg = file(PATH_CHATBOT . "a_settings.py") or exit("Cannot read". PATH_CHATBOT ."a_settings.py");
2421
foreach ($cfg as $j=>$line) { foreach ($replace as $k=>$v) { if (strpos($line, $k) !== false) {
2522
$cfg[$j] = "$k = $v # CHANGED BY INSTALLER\r\n";
2623
unset($replace[$k]);
2724
if (count($replace)==0) { break; }
2825
}}}
29-
try { file_put_contents(PATH_CHATBOT . "settings.py", implode("", $cfg)); }
30-
catch (Exception $ex) { exit("Error writing to ". PATH_CHATBOT . "settings.py"); }
26+
try { file_put_contents(PATH_CHATBOT . "a_settings.py", implode("", $cfg)); }
27+
catch (Exception $ex) { exit("Error writing to ". PATH_CHATBOT . "a_settings.py"); }
3128

3229
// (E) ADD AI TO CORE-CONFIG.PHP
3330
try {

ai chatbot/chatbot/0-setup.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
php 0-setup.php $1
1+
php 0-setup.php
22
virtualenv venv
33
source "venv/bin/activate"
44
pip install langchain transformers optimum auto-gptq chromadb sentence_transformers Flask pyjwt
5-
if [[ $1 == "GPU" ]]
5+
if [[ $1 == "CPU" ]]
66
then
7-
pip3 install torch torchvision torchaudio --force-reinstall
7+
pip install torch torchvision torchaudio --force-reinstall --index-url https://download.pytorch.org/whl/cpu
88
else
9-
pip3 install torch torchvision torchaudio --force-reinstall --index-url https://download.pytorch.org/whl/cpu
9+
pip install torch torchvision torchaudio --force-reinstall
1010
fi
11-
python create.py
12-
python bot.py
11+
python b_create.py
12+
python d_bot.py

ai chatbot/chatbot/1-create.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
@echo off
22
call venv\Scripts\activate
3-
python create.py
3+
python b_create.py
44
deactivate

ai chatbot/chatbot/1-create.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
source "venv/bin/activate"
2-
python create.py
2+
python b_create.py
33
deactivate

ai chatbot/chatbot/2-bot.bat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
@echo off
22
call venv\Scripts\activate
3-
python bot.py
3+
python d_bot.py
44
deactivate

ai chatbot/chatbot/2-bot.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
source "venv/bin/activate"
2-
python bot.py
2+
python d_bot.py
33
deactivate
Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,50 @@
1-
# (A) PATHS
1+
# (A) PATH
22
import os
33
path_base = os.path.dirname(os.path.realpath(__file__))
44
path_models = os.path.join(path_base, "models")
55
path_db = os.path.join(path_base, "db")
66
path_docs = os.path.join(path_base, "docs")
7+
8+
# (B) ENVIRONMENT VARIABLES
9+
os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "true"
710
os.environ["TRANSFORMERS_CACHE"] = path_models
811

9-
# (B) MODEL
10-
model_name = "TheBloke/Wizard-Vicuna-7B-Uncensored-GPTQ"
11-
model_sample = True
12-
model_max_tokens = 1024
13-
model_batch_size = 1
14-
model_temperature = 0.7
15-
model_top_p = 1
16-
model_top_k = 40
17-
model_sequences = 1
12+
# (C) MODEL SETTINGS
13+
model_name = "TheBloke/vicuna-7B-v1.5-GPTQ"
14+
model_args = {
15+
"do_sample" : True,
16+
"max_new_tokens" : 3000,
17+
"batch_size" : 1,
18+
"temperature" : 0.7,
19+
"top_k" : 40,
20+
"top_p" : 1,
21+
"num_return_sequences" : 1
22+
}
23+
24+
# (D) CHAIN SETTINGS
25+
chain_args = {
26+
"chain_type" : "stuff",
27+
"return_source_documents" : True,
28+
"verbose" : True
29+
}
1830

19-
# (C) PROMPT TEMPLATE
31+
# (E) PROMPT TEMPLATE
2032
prompt_template = """SYSTEM: Use the following context section and only that context to answer the question at the end. Do not use your internal knowledge. If you don't know the answer, just say that you don't know, don't try to make up an answer.
2133
CONTEXT: {context}
2234
USER: {question}
2335
ANSWER:"""
2436

25-
# (D) CHAIN
26-
chain_verbose = True
27-
chain_type = "stuff"
28-
chain_kwargs = 4
29-
chain_source = True
30-
31-
# (E) DATABASE
32-
doc_chunks = 512
33-
doc_overlap = 30
37+
# (F) DATABASE - DOCUMENT SPLITTER
38+
db_split = {
39+
"chunk_size" : 512,
40+
"chunk_overlap" : 30
41+
}
3442

35-
# (F) HTTP ENDPOINT
43+
# (G) HTTP ENDPOINT
3644
http_allow = ["http://localhost"]
3745
http_host = "localhost"
3846
http_port = 8008
3947

40-
# (G) JWT
48+
# (H) JWT
4149
jwt_algo = ""
4250
jwt_secret = ""

ai chatbot/chatbot/create.py renamed to ai chatbot/chatbot/b_create.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# (A) LOAD SETTINGS & MODULES
2-
import settings as set
2+
import a_settings as set
33
import os, glob
44
from pathlib import Path
55
from langchain.vectorstores import Chroma
@@ -65,9 +65,7 @@ def rmdir(folder):
6565
db.persist()
6666

6767
# (D2) ADD DOCUMENTS
68-
splitter = RecursiveCharacterTextSplitter(
69-
chunk_size = set.doc_chunks, chunk_overlap = set.doc_overlap
70-
)
68+
splitter = RecursiveCharacterTextSplitter(**set.db_split)
7169
for doc in all:
7270
print("Adding - " + doc)
7371
name, ext = os.path.splitext(doc)

ai chatbot/chatbot/c_tf.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# (A) LOAD SETTINGS & MODULES
2+
import a_settings as set
3+
import torch, psutil
4+
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer, pipeline
5+
from accelerate import infer_auto_device_map, init_empty_weights
6+
7+
# (B) HELPER - AUTO MAX MEMORY CALCULATION
8+
# credits : https://github.com/oobabooga/text-generation-webui/blob/main/modules/models.py
9+
def max_mem():
10+
# (B1) GPU MEMORY
11+
total = (torch.cuda.get_device_properties(0).total_memory / (1024 * 1024))
12+
suggestion = round((total - 1000) / 1000) * 1000
13+
if total - suggestion < 800:
14+
suggestion -= 1000
15+
suggestion = int(round(suggestion / 1000))
16+
max = { 0 : f"{suggestion}GiB" }
17+
18+
# (B2) CPU MEMORY
19+
total = (psutil.virtual_memory().available / (1024 * 1024))
20+
suggestion = round((total - 1000) / 1000) * 1000
21+
if total - suggestion < 800:
22+
suggestion -= 1000
23+
suggestion = int(round(suggestion / 1000))
24+
max["cpu"] = f"{suggestion}GiB"
25+
26+
# (B3) RETURN CALCULATED MEMORY
27+
return max
28+
29+
# (C) LOAD MODEL
30+
# (C1) INIT PARAMS
31+
params = {
32+
"low_cpu_mem_usage": True,
33+
"device_map" : "auto"
34+
}
35+
36+
# (C2) CPU ONLY
37+
if not any((torch.cuda.is_available(), torch.backends.mps.is_available())):
38+
params["torch_dtype"] = torch.float32
39+
40+
# (C3) GPU ACCELERATED
41+
else:
42+
config = AutoConfig.from_pretrained(set.model_name)
43+
with init_empty_weights():
44+
model = AutoModelForCausalLM.from_config(config)
45+
model.tie_weights()
46+
params["device_map"] = infer_auto_device_map(
47+
model,
48+
dtype = config.torch_dtype,
49+
max_memory = max_mem(),
50+
no_split_module_classes = model._no_split_modules
51+
)
52+
53+
# (C4) LOAD MODEL
54+
model = AutoModelForCausalLM.from_pretrained(set.model_name, **params)
55+
56+
# (D) PIPE
57+
pipe = pipeline(
58+
task = "text-generation",
59+
model = model,
60+
tokenizer = AutoTokenizer.from_pretrained(set.model_name),
61+
** set.model_args
62+
)

ai chatbot/chatbot/bot.py renamed to ai chatbot/chatbot/d_bot.py

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,37 @@
11
# (A) LOAD SETTINGS & MODULES
2-
import settings as set
2+
# (A1) SETTINGS & TRANSFORMER
3+
import a_settings as set
4+
import c_tf as tf
5+
6+
# (A2) FLASK
7+
# @TODO - ENABLE THIS TO OPEN FOR REGISTERED USERS ONLY
8+
# import jwt
39
from flask import Flask, Response, request
4-
import torch, jwt
5-
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
10+
11+
# (A3) LANGCHAIN
612
from langchain import PromptTemplate, HuggingFacePipeline
713
from langchain.vectorstores import Chroma
814
from langchain.embeddings import HuggingFaceEmbeddings
915
from langchain.chains import RetrievalQA
1016

11-
# (B) TOKENIZER + MODEL + DATABASE
12-
tokenizer = AutoTokenizer.from_pretrained(set.model_name)
13-
model = AutoModelForCausalLM.from_pretrained(
14-
set.model_name,
15-
torch_dtype = torch.float16,
16-
device_map = "auto"
17-
)
18-
db = Chroma(
19-
persist_directory = set.path_db,
20-
embedding_function = HuggingFaceEmbeddings()
21-
)
22-
23-
# (C) PIPE + CHAIN
24-
pipe = pipeline(
25-
task = "text-generation",
26-
model = model,
27-
tokenizer = tokenizer,
28-
eos_token_id = tokenizer.eos_token_id,
29-
do_sample = set.model_sample,
30-
max_new_tokens = set.model_max_tokens,
31-
batch_size = set.model_batch_size,
32-
temperature = set.model_temperature,
33-
top_k = set.model_top_k,
34-
top_p = set.model_top_p,
35-
num_return_sequences = set.model_sequences
36-
)
17+
# (B) CHAIN
3718
chain = RetrievalQA.from_chain_type(
38-
chain_type = set.chain_type,
39-
llm = HuggingFacePipeline(pipeline = pipe),
40-
retriever = db.as_retriever(search_kwargs = {"k": set.chain_kwargs}),
19+
llm = HuggingFacePipeline(pipeline = tf.pipe),
20+
retriever = Chroma(
21+
persist_directory = set.path_db,
22+
embedding_function = HuggingFaceEmbeddings()
23+
).as_retriever(),
4124
chain_type_kwargs = {
4225
"prompt": PromptTemplate (
4326
template = set.prompt_template,
4427
input_variables = ["question", "context"]
4528
)
4629
},
47-
return_source_documents = set.chain_source,
48-
verbose = set.chain_verbose
30+
** set.chain_args
4931
)
5032

5133
""" @TODO - ENABLE THIS TO OPEN FOR REGISTERED USERS ONLY
52-
# (D) VERIFY USER
34+
# (C) VERIFY USER
5335
def jwtVerify(cookies):
5436
try:
5537
token = jwt.decode(
@@ -66,19 +48,19 @@ def jwtVerify(cookies):
6648
return False
6749
"""
6850

69-
# (E) FLASK
51+
# (D) FLASK
7052
app = Flask(__name__)
7153
@app.route("/", methods = ["POST"])
7254
def bot():
73-
# (E1) CORS
55+
# (D1) CORS
7456
if "HTTP_ORIGIN" in request.environ and request.environ["HTTP_ORIGIN"] in set.http_allow:
75-
# (E1-1) ALLOW ONLY REGISTERED USERS
57+
# (D1-1) ALLOW ONLY REGISTERED USERS
7658
""" @TODO - ENABLE THIS TO OPEN FOR REGISTERED USERS ONLY
7759
if jwtVerify(request.cookies) is False:
7860
return Response("Not Allowed", status = 405)
7961
"""
8062

81-
# (E1-2) ANSWER THE QUESTION
63+
# (D1-2) ANSWER THE QUESTION
8264
data = dict(request.form)
8365
if "query" in data:
8466
ans = chain(data["query"])
@@ -89,12 +71,12 @@ def bot():
8971
response.headers.add("Access-Control-Allow-Origin", request.environ["HTTP_ORIGIN"] )
9072
response.headers.add("Access-Control-Allow-Credentials", "true")
9173

92-
# (E2) ORIGIN NOT ALLOWED
74+
# (D2) ORIGIN NOT ALLOWED
9375
else:
9476
response = Response("Not Allowed", status = 405)
9577
return response
9678

97-
# (F) GO!
79+
# (E) GO!
9880
if __name__ == "__main__":
9981
app.run(
10082
host = set.http_host,

0 commit comments

Comments
 (0)