ai_chatbot 0.1.6.5 → 0.1.6.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ai_chatbot/version.rb +1 -1
- data/lib/ml_model.py +90 -84
- metadata +4 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 751a6f2a9c762af2c03f9f14842ccafaefee38e2d0d2e9fb7f6595cfd1fc9a6c
|
4
|
+
data.tar.gz: 5030f83ae39a359a498bbbb2956bb5cffb79e7071b9b22c9f87c3bf67775a2dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba1346f5b15c8c08af48bfcd72490c060c34acb07112a63f6c2cc5589574195bc5dcf0b83c5471ef8d149a26b42a3ffbf7e6838aa3b28a42a12684bf4946fe5f
|
7
|
+
data.tar.gz: e652e1c7aa1cc2d80550832892b62a3d7018a13684f9c734f75b9ef73947c56e711569e3c99176dd71f3d65965ff10cd801d92b5c62010dfc8547279987b58b8
|
data/lib/ai_chatbot/version.rb
CHANGED
data/lib/ml_model.py
CHANGED
@@ -1,119 +1,125 @@
|
|
1
1
|
import sys
|
2
|
-
import
|
2
|
+
import pickle
|
3
3
|
import os
|
4
|
-
from dotenv import load_dotenv
|
5
4
|
from sklearn.feature_extraction.text import TfidfVectorizer
|
6
5
|
from sklearn.naive_bayes import MultinomialNB
|
7
6
|
from sklearn.pipeline import make_pipeline
|
8
7
|
from sklearn.metrics.pairwise import cosine_similarity
|
9
8
|
|
10
|
-
|
11
|
-
conn = psycopg2.connect(
|
12
|
-
dbname= os.getenv("DB_NAME"),
|
13
|
-
user=os.getenv("DB_USERNAME"),
|
14
|
-
password=os.getenv("DB_PASSWORD"),
|
15
|
-
host=os.getenv("DB_HOST"),
|
16
|
-
port=os.getenv("DB_PORT"),
|
17
|
-
)
|
18
|
-
cursor = conn.cursor()
|
9
|
+
DATA_FILE = "qa_model.pkl"
|
19
10
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
questions = [row[0] for row in rows]
|
25
|
-
answers = [row[1] for row in rows]
|
26
|
-
|
27
|
-
vectorizer = TfidfVectorizer()
|
28
|
-
question_vecs = vectorizer.fit_transform(questions) if questions else None
|
11
|
+
# Globals
|
12
|
+
questions = []
|
13
|
+
answers = []
|
14
|
+
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
|
29
15
|
|
16
|
+
# ------------------- Load or Initialize ------------------- #
|
17
|
+
def load_data():
|
18
|
+
global questions, answers
|
19
|
+
if os.path.exists(DATA_FILE):
|
20
|
+
with open(DATA_FILE, "rb") as f:
|
21
|
+
model_data = pickle.load(f)
|
22
|
+
questions = model_data.get('questions', [])
|
23
|
+
answers = model_data.get('answers', [])
|
24
|
+
print(f"Loaded {len(questions)} Q&A pairs.")
|
25
|
+
else:
|
26
|
+
# Default seed data
|
27
|
+
questions.extend([
|
28
|
+
"How to create a new model in Rails?",
|
29
|
+
"What is migration?",
|
30
|
+
"How to add a route?"
|
31
|
+
])
|
32
|
+
answers.extend([
|
33
|
+
"You can create a model using 'rails generate model'.",
|
34
|
+
"Migration is a database schema change.",
|
35
|
+
"You can add a route in the config/routes.rb file."
|
36
|
+
])
|
37
|
+
print("No existing model found, using default seed data.")
|
38
|
+
|
39
|
+
retrain_model()
|
40
|
+
|
41
|
+
def save_data():
|
42
|
+
with open(DATA_FILE, "wb") as f:
|
43
|
+
pickle.dump({'questions': questions, 'answers': answers}, f)
|
44
|
+
|
45
|
+
def retrain_model():
|
46
|
+
if questions and answers:
|
47
|
+
model.fit(questions, answers)
|
30
48
|
|
49
|
+
# ------------------- Core Actions ------------------- #
|
31
50
|
def get_prediction(query):
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
similarities = cosine_similarity(query_vec, question_vecs).flatten()
|
51
|
+
query_vec = model.named_steps['tfidfvectorizer'].transform([query])
|
52
|
+
question_vecs = model.named_steps['tfidfvectorizer'].transform(questions)
|
53
|
+
similarities = cosine_similarity(query_vec, question_vecs)
|
54
|
+
max_similarity = similarities.max()
|
37
55
|
|
38
|
-
|
39
|
-
max_similarity = similarities[max_sim_index]
|
56
|
+
print(f"🔍 Similarity Score: {max_similarity:.2f}")
|
40
57
|
|
41
58
|
threshold = 0.65
|
42
59
|
if max_similarity < threshold:
|
43
|
-
return "No good match found.
|
60
|
+
return "No good match found. You may need to train the model with this question."
|
44
61
|
else:
|
45
|
-
|
62
|
+
prediction = model.predict([query])
|
63
|
+
return prediction[0]
|
46
64
|
|
47
|
-
# Function to train the model with new data
|
48
65
|
def train_model(new_question, new_answer):
|
49
|
-
global questions, answers
|
50
|
-
|
51
|
-
# Store in database
|
52
|
-
|
53
|
-
cursor.execute("INSERT INTO qa_data (question, answer, created_at,updated_at) VALUES (%s, %s, NOW(),NOW()) ON CONFLICT (question) DO NOTHING",
|
54
|
-
(new_question, new_answer))
|
55
|
-
conn.commit()
|
56
|
-
|
57
|
-
# Update lists and retrain model
|
58
66
|
questions.append(new_question)
|
59
67
|
answers.append(new_answer)
|
60
|
-
|
61
|
-
|
62
|
-
return f"
|
68
|
+
retrain_model()
|
69
|
+
save_data()
|
70
|
+
return f"Model updated with: '{new_question}'"
|
63
71
|
|
64
|
-
# Function to update an answer
|
65
72
|
def update_answer(existing_question, new_answer):
|
66
|
-
cursor.execute("UPDATE qa_data SET answer = %s WHERE question = %s", (new_answer, existing_question))
|
67
|
-
conn.commit()
|
68
|
-
|
69
|
-
# Update lists and retrain model
|
70
|
-
index = questions.index(existing_question)
|
71
|
-
answers[index] = new_answer
|
72
|
-
model.fit(questions, answers)
|
73
|
-
|
74
|
-
return f"Updated: '{existing_question}' -> '{new_answer}'"
|
75
|
-
|
76
|
-
# Function to delete a question
|
77
|
-
def delete_question(existing_question):
|
78
|
-
cursor.execute("DELETE FROM qa_data WHERE question = %s", (existing_question,))
|
79
|
-
conn.commit()
|
80
|
-
|
81
73
|
if existing_question in questions:
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
return
|
74
|
+
idx = questions.index(existing_question)
|
75
|
+
answers[idx] = new_answer
|
76
|
+
retrain_model()
|
77
|
+
save_data()
|
78
|
+
return f"Answer updated for: '{existing_question}'"
|
79
|
+
return "Question not found."
|
80
|
+
|
81
|
+
def update_or_delete_question(existing_question, new_question=None):
|
82
|
+
if existing_question in questions:
|
83
|
+
idx = questions.index(existing_question)
|
84
|
+
if new_question:
|
85
|
+
questions[idx] = new_question
|
86
|
+
action = f"Question updated to: '{new_question}'"
|
87
|
+
else:
|
88
|
+
questions.pop(idx)
|
89
|
+
answers.pop(idx)
|
90
|
+
action = f"🗑️ Question '{existing_question}' deleted."
|
91
|
+
retrain_model()
|
92
|
+
save_data()
|
93
|
+
return action
|
94
|
+
return "Question not found."
|
88
95
|
|
89
|
-
# Function to list questions
|
90
96
|
def list_questions():
|
91
|
-
|
92
|
-
return [row[0] for row in cursor.fetchall()]
|
97
|
+
return "\n".join([f"{i+1}. {q}" for i, q in enumerate(questions)])
|
93
98
|
|
94
|
-
# Function to list answers
|
95
99
|
def list_answers():
|
96
|
-
|
97
|
-
return [row[0] for row in cursor.fetchall()]
|
100
|
+
return "\n".join([f"{i+1}. {a}" for i, a in enumerate(answers)])
|
98
101
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
answer = sys.argv[3] if len(sys.argv) > 3 else None
|
102
|
+
# ------------------- CLI Entry ------------------- #
|
103
|
+
def main(action, query=None, answer=None):
|
104
|
+
load_data()
|
103
105
|
|
104
106
|
if action == "predict":
|
105
|
-
|
107
|
+
return get_prediction(query)
|
106
108
|
elif action == "train_model":
|
107
|
-
|
109
|
+
return train_model(query, answer)
|
108
110
|
elif action == "update_answer":
|
109
|
-
|
110
|
-
elif action == "
|
111
|
-
|
111
|
+
return update_answer(query, answer)
|
112
|
+
elif action == "update_or_delete_question":
|
113
|
+
return update_or_delete_question(query, answer)
|
112
114
|
elif action == "list_questions":
|
113
|
-
|
115
|
+
return list_questions()
|
114
116
|
elif action == "list_answers":
|
115
|
-
|
117
|
+
return list_answers()
|
118
|
+
else:
|
119
|
+
return "Unknown action. Try: predict, train_model, update_answer, list_questions, list_answers"
|
116
120
|
|
117
|
-
|
118
|
-
|
119
|
-
|
121
|
+
if __name__ == "__main__":
|
122
|
+
action = sys.argv[1] if len(sys.argv) > 1 else None
|
123
|
+
question = sys.argv[2] if len(sys.argv) > 2 else None
|
124
|
+
answer = sys.argv[3] if len(sys.argv) > 3 else None
|
125
|
+
print(main(action, question, answer))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ai_chatbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.6.5
|
4
|
+
version: 0.1.6.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sanket
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-06-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: open3
|
@@ -24,8 +24,7 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
description:
|
28
|
-
time. Also updated unanswered question logging.
|
27
|
+
description: fixed model error. Version 0.1.6.5.1 Details on Git.
|
29
28
|
email:
|
30
29
|
- sanket.tikhande@gmail.com
|
31
30
|
executables: []
|
@@ -58,5 +57,5 @@ requirements: []
|
|
58
57
|
rubygems_version: 3.3.7
|
59
58
|
signing_key:
|
60
59
|
specification_version: 4
|
61
|
-
summary: 'Fix: Added
|
60
|
+
summary: 'Fix: Added high accuracy'
|
62
61
|
test_files: []
|