ai_chatbot 0.1.6.5.1 → 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 +92 -109
- 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,142 +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
|
-
load_dotenv()
|
12
|
-
|
13
|
-
# Connect to PostgreSQL
|
14
|
-
conn = psycopg2.connect(
|
15
|
-
dbname=os.getenv("DB_NAME"),
|
16
|
-
user=os.getenv("DB_USERNAME"),
|
17
|
-
password=os.getenv("DB_PASSWORD"),
|
18
|
-
host=os.getenv("DB_HOST"),
|
19
|
-
port=os.getenv("DB_PORT"),
|
20
|
-
)
|
21
|
-
cursor = conn.cursor()
|
22
|
-
|
23
|
-
# Fetch data from DB
|
24
|
-
cursor.execute("SELECT question, answer FROM qa_data")
|
25
|
-
rows = cursor.fetchall()
|
26
|
-
questions = [row[0] for row in rows]
|
27
|
-
answers = [row[1] for row in rows]
|
28
|
-
|
29
|
-
# Define the vectorizer and Naive Bayes model
|
30
|
-
vectorizer = TfidfVectorizer()
|
31
|
-
model = MultinomialNB()
|
32
|
-
pipeline = make_pipeline(vectorizer, model)
|
33
|
-
|
34
|
-
# Train model if there is data
|
35
|
-
if questions:
|
36
|
-
pipeline.fit(questions, answers)
|
9
|
+
DATA_FILE = "qa_model.pkl"
|
37
10
|
|
11
|
+
# Globals
|
12
|
+
questions = []
|
13
|
+
answers = []
|
14
|
+
model = make_pipeline(TfidfVectorizer(), MultinomialNB())
|
38
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)
|
48
|
+
|
49
|
+
# ------------------- Core Actions ------------------- #
|
39
50
|
def get_prediction(query):
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
similarities = cosine_similarity(query_vec, vectorizer.transform(questions)).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()
|
45
55
|
|
46
|
-
|
47
|
-
max_similarity = similarities[max_sim_index]
|
56
|
+
print(f"🔍 Similarity Score: {max_similarity:.2f}")
|
48
57
|
|
49
58
|
threshold = 0.65
|
50
59
|
if max_similarity < threshold:
|
51
|
-
return "No good match found.
|
60
|
+
return "No good match found. You may need to train the model with this question."
|
52
61
|
else:
|
53
|
-
|
62
|
+
prediction = model.predict([query])
|
63
|
+
return prediction[0]
|
54
64
|
|
55
|
-
|
56
|
-
# Function to train the model with new data
|
57
65
|
def train_model(new_question, new_answer):
|
58
|
-
global questions, answers
|
59
|
-
|
60
|
-
# Store in database
|
61
|
-
cursor.execute(
|
62
|
-
"INSERT INTO qa_data (question, answer, created_at, updated_at) VALUES (%s, %s, NOW(), NOW()) ON CONFLICT (question) DO NOTHING",
|
63
|
-
(new_question, new_answer),
|
64
|
-
)
|
65
|
-
conn.commit()
|
66
|
-
|
67
|
-
# Update lists and retrain model
|
68
66
|
questions.append(new_question)
|
69
67
|
answers.append(new_answer)
|
70
|
-
|
71
|
-
|
72
|
-
return f"
|
73
|
-
|
68
|
+
retrain_model()
|
69
|
+
save_data()
|
70
|
+
return f"Model updated with: '{new_question}'"
|
74
71
|
|
75
|
-
# Function to update an answer
|
76
72
|
def update_answer(existing_question, new_answer):
|
77
|
-
if existing_question
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
73
|
+
if existing_question in questions:
|
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."
|
91
95
|
|
92
|
-
|
93
|
-
# Function to delete a question
|
94
|
-
def delete_question(existing_question):
|
95
|
-
if existing_question not in questions:
|
96
|
-
return f"Question '{existing_question}' not found."
|
97
|
-
|
98
|
-
cursor.execute("DELETE FROM qa_data WHERE question = %s", (existing_question,))
|
99
|
-
conn.commit()
|
100
|
-
|
101
|
-
index = questions.index(existing_question)
|
102
|
-
del questions[index]
|
103
|
-
del answers[index]
|
104
|
-
pipeline.fit(questions, answers)
|
105
|
-
|
106
|
-
return f"Deleted: '{existing_question}'"
|
107
|
-
|
108
|
-
|
109
|
-
# Function to list questions
|
110
96
|
def list_questions():
|
111
|
-
|
112
|
-
return [row[0] for row in cursor.fetchall()]
|
97
|
+
return "\n".join([f"{i+1}. {q}" for i, q in enumerate(questions)])
|
113
98
|
|
114
|
-
|
115
|
-
# Function to list answers
|
116
99
|
def list_answers():
|
117
|
-
|
118
|
-
return [row[0] for row in cursor.fetchall()]
|
100
|
+
return "\n".join([f"{i+1}. {a}" for i, a in enumerate(answers)])
|
119
101
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
action = sys.argv[1]
|
124
|
-
question = sys.argv[2] if len(sys.argv) > 2 else None
|
125
|
-
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()
|
126
105
|
|
127
106
|
if action == "predict":
|
128
|
-
|
107
|
+
return get_prediction(query)
|
129
108
|
elif action == "train_model":
|
130
|
-
|
109
|
+
return train_model(query, answer)
|
131
110
|
elif action == "update_answer":
|
132
|
-
|
133
|
-
elif action == "
|
134
|
-
|
111
|
+
return update_answer(query, answer)
|
112
|
+
elif action == "update_or_delete_question":
|
113
|
+
return update_or_delete_question(query, answer)
|
135
114
|
elif action == "list_questions":
|
136
|
-
|
115
|
+
return list_questions()
|
137
116
|
elif action == "list_answers":
|
138
|
-
|
117
|
+
return list_answers()
|
118
|
+
else:
|
119
|
+
return "Unknown action. Try: predict, train_model, update_answer, list_questions, list_answers"
|
139
120
|
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
on Git.
|
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: []
|