ai_chatbot 0.1.6.4 → 0.1.6.5.1

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: 802b2da58aa8ef78b7b9813faf48e789db953d4c6e62a437002b1dc6017e4167
4
- data.tar.gz: b3525c33bd0965aeccc2b13d67380e67df0ec6f3c502b0afe24968d694833d0f
3
+ metadata.gz: fee6347bdac6070db1bfbc1711f039103c1237c149f42a7f0b848b85eba98314
4
+ data.tar.gz: c54c29b235f159921d4b33ac36bc56d505983eb03a25500829d72e9741628983
5
5
  SHA512:
6
- metadata.gz: eff5b8d9885206a83a4f6a05ca275c925717299360ae62df2af451009a07e5d244de503031d0f8a091a4cd1ef067af85a18cadf6ded9ef994899b6d5639e4747
7
- data.tar.gz: b05cd753b9e3d4db2d9e6fb86aa43b2e0e355d8cf723e3d852bfb43a23897aade4d5849232179c7338babc0f19568d05d5c5eafdda781337d00daca83b6594fc
6
+ metadata.gz: f80cec458575b77b6be9879007e3f60830886b88bcfbde28974e9fe1e68b59d61424956a0aea0db62e69e5705e2c8d2b2b97d3e6700ee3b9cdd141530816325f
7
+ data.tar.gz: d2ac4f47ef9ce02255a60e84a6bff019c9036be5368f79e43748ca945a459c4459c5b53ee7a57a8345114018e03d0f5a1eeab70895ac78bafefed6980223dc59
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AiChatbot
4
- VERSION = "0.1.6.4"
4
+ VERSION = "0.1.6.5.1"
5
5
  end
data/lib/ml_model.py CHANGED
@@ -1,162 +1,142 @@
1
1
  import sys
2
+ import psycopg2
3
+ import os
4
+ from dotenv import load_dotenv
2
5
  from sklearn.feature_extraction.text import TfidfVectorizer
3
6
  from sklearn.naive_bayes import MultinomialNB
4
7
  from sklearn.pipeline import make_pipeline
5
8
  from sklearn.metrics.pairwise import cosine_similarity
6
- import pickle
7
- import os
8
9
 
9
- # Cache to store previously asked questions
10
- cache = {}
11
-
12
- # Load or initialize the dataset
13
- if os.path.exists("qa_model.pkl"):
14
- with open("qa_model.pkl", "rb") as f:
15
- model_data = pickle.load(f)
16
- questions = model_data.get("questions", [])
17
- answers = model_data.get("answers", [])
18
- else:
19
- questions = [
20
- "How to create a new model in Rails?",
21
- "What is migration?",
22
- "How to add a route?"
23
- ]
24
- answers = [
25
- "You can create a model using 'rails generate model'.",
26
- "Migration is a database schema change.",
27
- "You can add a route in the config/routes.rb file."
28
- ]
29
-
30
- # Create a pipeline (TF-IDF + MultinomialNB)
31
- model = make_pipeline(TfidfVectorizer(), MultinomialNB())
32
- model.fit(questions, answers)
33
-
34
- # Populate cache with known questions and answers
35
- for q, a in zip(questions, answers):
36
- cache[q.lower()] = a
37
-
38
- # Function to predict or retrain the model
39
- def main(action, query=None, new_answer=None):
40
- if action == "predict":
41
- return get_prediction(query)
42
- elif action == "train_model":
43
- return train_model(query, new_answer)
44
- elif action == "update_answer":
45
- return update_answer(query, new_answer)
46
- elif action == "update_or_delete_question":
47
- return update_or_delete_question(query, new_answer)
48
- elif action == "list_questions":
49
- return list_questions()
50
- elif action == "list_answers":
51
- return list_answers()
10
+ # Load environment variables
11
+ load_dotenv()
52
12
 
53
- # Function to predict the response with caching
54
- def get_prediction(query):
55
- query_lower = query.lower()
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]
56
28
 
57
- # **Check cache first**
58
- if query_lower in cache:
59
- return cache[query_lower]
29
+ # Define the vectorizer and Naive Bayes model
30
+ vectorizer = TfidfVectorizer()
31
+ model = MultinomialNB()
32
+ pipeline = make_pipeline(vectorizer, model)
60
33
 
61
- query_vec = model.named_steps['tfidfvectorizer'].transform([query])
62
- question_vecs = model.named_steps['tfidfvectorizer'].transform(questions)
34
+ # Train model if there is data
35
+ if questions:
36
+ pipeline.fit(questions, answers)
63
37
 
64
- # Calculate cosine similarity
65
- similarities = cosine_similarity(query_vec, question_vecs)
66
- max_similarity = similarities.max()
38
+
39
+ def get_prediction(query):
40
+ if not questions:
41
+ return "No questions available in the database."
42
+
43
+ query_vec = vectorizer.transform([query])
44
+ similarities = cosine_similarity(query_vec, vectorizer.transform(questions)).flatten()
45
+
46
+ max_sim_index = similarities.argmax()
47
+ max_similarity = similarities[max_sim_index]
67
48
 
68
49
  threshold = 0.65
69
50
  if max_similarity < threshold:
70
51
  return "No good match found. Please provide the correct answer."
71
52
  else:
72
- prediction = model.predict([query])[0]
73
-
74
- # **Store in cache for faster future retrieval**
75
- cache[query_lower] = prediction
76
-
77
- return prediction
78
-
79
- # Function to train the model with a new question and answer
53
+ return answers[max_sim_index]
54
+
55
+
56
+ # Function to train the model with new data
80
57
  def train_model(new_question, new_answer):
81
58
  global questions, answers
82
59
 
83
- # Append new question-answer pair
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
84
68
  questions.append(new_question)
85
69
  answers.append(new_answer)
70
+ pipeline.fit(questions, answers) # Retrain model
86
71
 
87
- # Retrain the model
88
- model.fit(questions, answers)
89
-
90
- # **Update cache**
91
- cache[new_question.lower()] = new_answer
72
+ return f"Added: '{new_question}' -> '{new_answer}'"
92
73
 
93
- # Save the updated model
94
- with open("qa_model.pkl", "wb") as f:
95
- pickle.dump({"questions": questions, "answers": answers}, f)
96
-
97
- return f"Model retrained with: '{new_question}' -> '{new_answer}'"
98
74
 
99
75
  # Function to update an answer
100
76
  def update_answer(existing_question, new_answer):
101
- global questions, answers
102
-
103
- if existing_question in questions:
104
- index = questions.index(existing_question)
105
- answers[index] = new_answer
106
-
107
- # Retrain the model
108
- model.fit(questions, answers)
77
+ if existing_question not in questions:
78
+ return f"Question '{existing_question}' not found."
109
79
 
110
- # **Update cache**
111
- cache[existing_question.lower()] = new_answer
80
+ cursor.execute(
81
+ "UPDATE qa_data SET answer = %s WHERE question = %s", (new_answer, existing_question)
82
+ )
83
+ conn.commit()
112
84
 
113
- # Save the model
114
- with open("qa_model.pkl", "wb") as f:
115
- pickle.dump({"questions": questions, "answers": answers}, f)
116
-
117
- return f"Answer updated for: '{existing_question}'"
118
-
119
- return "Question not found."
120
-
121
- # Function to update or delete a question
122
- def update_or_delete_question(existing_question, new_question):
123
- global questions, answers
85
+ # Update lists and retrain model
86
+ index = questions.index(existing_question)
87
+ answers[index] = new_answer
88
+ pipeline.fit(questions, answers)
124
89
 
125
- if existing_question in questions:
126
- index = questions.index(existing_question)
90
+ return f"Updated: '{existing_question}' -> '{new_answer}'"
127
91
 
128
- if new_question:
129
- questions[index] = new_question
130
- # **Update cache**
131
- cache[new_question.lower()] = answers[index]
132
- else:
133
- # Delete the question
134
- del questions[index]
135
- del answers[index]
136
92
 
137
- # Retrain the model
138
- model.fit(questions, answers)
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."
139
97
 
140
- # **Remove from cache if deleted**
141
- if not new_question:
142
- cache.pop(existing_question.lower(), None)
98
+ cursor.execute("DELETE FROM qa_data WHERE question = %s", (existing_question,))
99
+ conn.commit()
143
100
 
144
- # Save the model
145
- with open("qa_model.pkl", "wb") as f:
146
- pickle.dump({"questions": questions, "answers": answers}, f)
101
+ index = questions.index(existing_question)
102
+ del questions[index]
103
+ del answers[index]
104
+ pipeline.fit(questions, answers)
147
105
 
148
- return f"Updated question: '{existing_question}' -> '{new_question}'" if new_question else f"Deleted: '{existing_question}'"
106
+ return f"Deleted: '{existing_question}'"
149
107
 
150
- return "Question not found."
151
108
 
109
+ # Function to list questions
152
110
  def list_questions():
153
- return questions
111
+ cursor.execute("SELECT question FROM qa_data")
112
+ return [row[0] for row in cursor.fetchall()]
113
+
154
114
 
115
+ # Function to list answers
155
116
  def list_answers():
156
- return answers
117
+ cursor.execute("SELECT answer FROM qa_data")
118
+ return [row[0] for row in cursor.fetchall()]
157
119
 
120
+
121
+ # Command-line execution
158
122
  if __name__ == "__main__":
159
123
  action = sys.argv[1]
160
124
  question = sys.argv[2] if len(sys.argv) > 2 else None
161
125
  answer = sys.argv[3] if len(sys.argv) > 3 else None
162
- print(main(action, question, answer))
126
+
127
+ if action == "predict":
128
+ print(get_prediction(question))
129
+ elif action == "train_model":
130
+ print(train_model(question, answer))
131
+ elif action == "update_answer":
132
+ print(update_answer(question, answer))
133
+ elif action == "delete_question":
134
+ print(delete_question(question))
135
+ elif action == "list_questions":
136
+ print(list_questions())
137
+ elif action == "list_answers":
138
+ print(list_answers())
139
+
140
+ # Close DB connection
141
+ cursor.close()
142
+ conn.close()
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.4
4
+ version: 0.1.6.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sanket
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-26 00:00:00.000000000 Z
11
+ date: 2025-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: open3
@@ -24,8 +24,8 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- description: Integrates a chatbot using Python for predictions and training in a Rails
28
- application.
27
+ description: Added PostgreSQL support, fixed model error. Version 0.1.6.5.1 Details
28
+ on Git.
29
29
  email:
30
30
  - sanket.tikhande@gmail.com
31
31
  executables: []
@@ -58,5 +58,5 @@ requirements: []
58
58
  rubygems_version: 3.3.7
59
59
  signing_key:
60
60
  specification_version: 4
61
- summary: 'Fix: Improved response caching in ChatbotService'
61
+ summary: 'Fix: Added postgres integration'
62
62
  test_files: []