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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65a8e9d42a6f0a36cff9a13f7428975232912b4a2decd9509772b38d849f6340
4
- data.tar.gz: f54c3642f29fb1f12f49af978f4ae34bd9dcdb85c7d22b48d07b195384354832
3
+ metadata.gz: 751a6f2a9c762af2c03f9f14842ccafaefee38e2d0d2e9fb7f6595cfd1fc9a6c
4
+ data.tar.gz: 5030f83ae39a359a498bbbb2956bb5cffb79e7071b9b22c9f87c3bf67775a2dd
5
5
  SHA512:
6
- metadata.gz: 6a7d4c388b24487a16a51dac935eb54d6dfc384f8d478dd80667f5de9a3dc2d8140e2785a0721db5d93f79b29285ad5deb3462042238536925c5b8e66b481939
7
- data.tar.gz: e691cb235c3cae3c9b754c30a20cec05ca17bc684e0e7c727528ccf0b8f06e4a100e4473d050793a62029bf3352425f1b1c00540e709f04f868366bb2a0f5559
6
+ metadata.gz: ba1346f5b15c8c08af48bfcd72490c060c34acb07112a63f6c2cc5589574195bc5dcf0b83c5471ef8d149a26b42a3ffbf7e6838aa3b28a42a12684bf4946fe5f
7
+ data.tar.gz: e652e1c7aa1cc2d80550832892b62a3d7018a13684f9c734f75b9ef73947c56e711569e3c99176dd71f3d65965ff10cd801d92b5c62010dfc8547279987b58b8
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AiChatbot
4
- VERSION = "0.1.6.5"
4
+ VERSION = "0.1.6.5.2"
5
5
  end
data/lib/ml_model.py CHANGED
@@ -1,119 +1,125 @@
1
1
  import sys
2
- import psycopg2
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
- # Connect to PostgreSQL
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
- db_name = os.getenv("DB_NAME")
21
-
22
- cursor.execute("SELECT question, answer FROM qa_data")
23
- rows = cursor.fetchall()
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
- if not questions:
33
- return "No questions available in the database."
34
-
35
- query_vec = vectorizer.transform([query])
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
- max_sim_index = similarities.argmax()
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. Please provide the correct answer."
60
+ return "No good match found. You may need to train the model with this question."
44
61
  else:
45
- return answers[max_sim_index]
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
- model.fit(questions, answers)
61
-
62
- return f"Added: '{new_question}' -> '{new_answer}'"
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
- index = questions.index(existing_question)
83
- del questions[index]
84
- del answers[index]
85
- model.fit(questions, answers)
86
-
87
- return f"Deleted: '{existing_question}'"
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
- cursor.execute("SELECT question FROM qa_data")
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
- cursor.execute("SELECT answer FROM qa_data")
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
- if __name__ == "__main__":
100
- action = sys.argv[1]
101
- question = sys.argv[2] if len(sys.argv) > 2 else None
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
- print(get_prediction(question))
107
+ return get_prediction(query)
106
108
  elif action == "train_model":
107
- print(train_model(question, answer))
109
+ return train_model(query, answer)
108
110
  elif action == "update_answer":
109
- print(update_answer(question, answer))
110
- elif action == "delete_question":
111
- print(delete_question(question))
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
- print(list_questions())
115
+ return list_questions()
114
116
  elif action == "list_answers":
115
- print(list_answers())
117
+ return list_answers()
118
+ else:
119
+ return "Unknown action. Try: predict, train_model, update_answer, list_questions, list_answers"
116
120
 
117
- # Close DB connection
118
- cursor.close()
119
- conn.close()
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-03-04 00:00:00.000000000 Z
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: Added caching to avoid redundant DB queries, improving chatbot response
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 postgres integration'
60
+ summary: 'Fix: Added high accuracy'
62
61
  test_files: []