dynamic-learning-model 1.2.6__tar.gz → 1.3__tar.gz
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.
- {dynamic_learning_model-1.2.6/dynamic_learning_model.egg-info → dynamic_learning_model-1.3}/PKG-INFO +1 -1
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dlm/DLM.py +325 -150
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3/dynamic_learning_model.egg-info}/PKG-INFO +1 -1
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/setup.py +1 -1
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/LICENSE +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/README.md +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dlm/__init__.py +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dynamic_learning_model.egg-info/SOURCES.txt +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dynamic_learning_model.egg-info/dependency_links.txt +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dynamic_learning_model.egg-info/requires.txt +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/dynamic_learning_model.egg-info/top_level.txt +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/pyproject.toml +0 -0
- {dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3}/setup.cfg +0 -0
{dynamic_learning_model-1.2.6/dynamic_learning_model.egg-info → dynamic_learning_model-1.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dynamic-learning-model
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3
|
|
4
4
|
Summary: A Dynamic Learning Model for processing NLP queries using hybrid AI and reasoning.
|
|
5
5
|
Home-page: https://github.com/VigneshT24/Dynamic_Learning_Model
|
|
6
6
|
Author: Vignesh Thondikulam
|
|
@@ -7,6 +7,7 @@ import time
|
|
|
7
7
|
import sqlite3
|
|
8
8
|
import re
|
|
9
9
|
import nltk
|
|
10
|
+
import math
|
|
10
11
|
from better_profanity import profanity
|
|
11
12
|
from nltk.corpus import names
|
|
12
13
|
from word2number import w2n
|
|
@@ -213,6 +214,40 @@ class DLM:
|
|
|
213
214
|
]
|
|
214
215
|
}
|
|
215
216
|
|
|
217
|
+
# to solve geometric problems (advanced CoT)
|
|
218
|
+
__geometric_calculation_identifiers = {
|
|
219
|
+
"triangle": {
|
|
220
|
+
"keywords": ["area", "triangle"], # keyword that will be mentioned while in CoT
|
|
221
|
+
"params": ["base", "height"], # helps with formula
|
|
222
|
+
"formula": lambda d: 0.5 * d["base"] * d["height"] # formula to solve geometric problem
|
|
223
|
+
},
|
|
224
|
+
"rectangle": {
|
|
225
|
+
"keywords": ["area", "rectangle"],
|
|
226
|
+
"params": ["length", "width"],
|
|
227
|
+
"formula": lambda d: d["length"] * d["width"]
|
|
228
|
+
},
|
|
229
|
+
"parallelogram": {
|
|
230
|
+
"keywords": ["area", "parallelogram"],
|
|
231
|
+
"params": ["base", "height"],
|
|
232
|
+
"formula": lambda d: d["base"] * d["height"]
|
|
233
|
+
},
|
|
234
|
+
"square": {
|
|
235
|
+
"keywords": ["area", "square"],
|
|
236
|
+
"params": ["side"],
|
|
237
|
+
"formula": lambda d: math.pow(d["side"], 2)
|
|
238
|
+
},
|
|
239
|
+
"trapezoid": {
|
|
240
|
+
"keywords": ["area", "trapezoid"],
|
|
241
|
+
"params": ["other", "height"],
|
|
242
|
+
"formula": lambda d: 0.5 * (d["other"][0] + d["other"][1]) * d["height"]
|
|
243
|
+
},
|
|
244
|
+
"circle": {
|
|
245
|
+
"keywords": ["area", "circle"],
|
|
246
|
+
"params": ["radius"],
|
|
247
|
+
"formula": lambda d: (math.pow(d["radius"], 2)) * math.pi
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
216
251
|
# for SI conversion and CoT
|
|
217
252
|
__units = {
|
|
218
253
|
# distance units (base = meters)
|
|
@@ -534,6 +569,131 @@ class DLM:
|
|
|
534
569
|
else:
|
|
535
570
|
self.__tone = ""
|
|
536
571
|
|
|
572
|
+
def __geometric_calculation(self, filtered_query, display_thought):
|
|
573
|
+
"""
|
|
574
|
+
Perform formula-related math and geometric problems that will be called inside perform_advanced_CoT
|
|
575
|
+
|
|
576
|
+
Parameters:
|
|
577
|
+
filtered_query (str): user query that has been filtered to have mostly computational details
|
|
578
|
+
display_thought (bool): Indicates whether the user wants to have the bot display its thought process or just give the answer
|
|
579
|
+
|
|
580
|
+
Returns:
|
|
581
|
+
float: The result after computing the formula calculation
|
|
582
|
+
|
|
583
|
+
Behavior:
|
|
584
|
+
- Search through query to find specific keywords like 'area' or 'volume'
|
|
585
|
+
- Then, search to find shape or object to perform math on like 'triangle' or 'square'
|
|
586
|
+
- Find numbers associated with object details and store in appropriate list
|
|
587
|
+
- Finally, find appropriate formula with identifiers and plug in and return answer
|
|
588
|
+
|
|
589
|
+
"""
|
|
590
|
+
height_value = None
|
|
591
|
+
height_value_index = None
|
|
592
|
+
other_values = []
|
|
593
|
+
object_intel = []
|
|
594
|
+
common_endings = ["ular", "ish", "al"] # some people might say "squarish" or "rectangular" etc
|
|
595
|
+
|
|
596
|
+
tokens = filtered_query.split()
|
|
597
|
+
lower_tokens = [t.lower() for t in tokens]
|
|
598
|
+
|
|
599
|
+
# set height value number (if exists) to height_value and also its corresponding index
|
|
600
|
+
for idx, token in enumerate(lower_tokens):
|
|
601
|
+
is_similar = difflib.get_close_matches(token, ["height"], n=1, cutoff=0.7)
|
|
602
|
+
if is_similar and is_similar[0] == "height":
|
|
603
|
+
# try token before
|
|
604
|
+
if idx > 0:
|
|
605
|
+
candidate = lower_tokens[idx - 1]
|
|
606
|
+
try:
|
|
607
|
+
height_value = w2n.word_to_num(candidate)
|
|
608
|
+
height_value_index = idx - 1
|
|
609
|
+
except ValueError:
|
|
610
|
+
if candidate.replace('.', '', 1).isdigit():
|
|
611
|
+
height_value = float(candidate)
|
|
612
|
+
height_value_index = idx - 1
|
|
613
|
+
break
|
|
614
|
+
pass
|
|
615
|
+
|
|
616
|
+
# try token after
|
|
617
|
+
if idx < len(tokens) - 1:
|
|
618
|
+
candidate = lower_tokens[idx + 1]
|
|
619
|
+
try:
|
|
620
|
+
height_value = w2n.word_to_num(candidate)
|
|
621
|
+
height_value_index = idx + 1
|
|
622
|
+
except ValueError:
|
|
623
|
+
if candidate.replace('.', '', 1).isdigit():
|
|
624
|
+
height_value = float(candidate)
|
|
625
|
+
height_value_index = idx + 1
|
|
626
|
+
break
|
|
627
|
+
pass
|
|
628
|
+
|
|
629
|
+
# append all other numbers to "other_value" list
|
|
630
|
+
for i, token in enumerate(tokens):
|
|
631
|
+
if i == height_value_index:
|
|
632
|
+
continue # skip the height value itself
|
|
633
|
+
try:
|
|
634
|
+
num = w2n.word_to_num(token)
|
|
635
|
+
other_values.append(num)
|
|
636
|
+
except ValueError:
|
|
637
|
+
if token.replace('.', '', 1).isdigit():
|
|
638
|
+
other_values.append(float(token))
|
|
639
|
+
|
|
640
|
+
# find the object name and what to compute about the object
|
|
641
|
+
for token in lower_tokens:
|
|
642
|
+
for obj in self.__geometric_calculation_identifiers:
|
|
643
|
+
# remove common endings for better matching
|
|
644
|
+
for ending in common_endings:
|
|
645
|
+
if token.endswith(ending):
|
|
646
|
+
token = token[: -len(ending)] # Remove the ending
|
|
647
|
+
break
|
|
648
|
+
is_similar = difflib.get_close_matches(token, [obj], n=1, cutoff=0.80)
|
|
649
|
+
if is_similar and is_similar[0] == obj:
|
|
650
|
+
object_intel.extend(self.__geometric_calculation_identifiers[obj]["keywords"])
|
|
651
|
+
|
|
652
|
+
obj_name = object_intel[1]
|
|
653
|
+
if display_thought:
|
|
654
|
+
self.__loadingAnimation(f"It seems that the user wants to compute the {' of a '.join(object_intel)}", 0.5)
|
|
655
|
+
if height_value is not None:
|
|
656
|
+
self.__loadingAnimation(f"* The user has mentioned that the height of the {obj_name} object is {height_value}", 0.4)
|
|
657
|
+
else:
|
|
658
|
+
self.__loadingAnimation(f"* The {object_intel[1]} object has no height associated with it, so moving on", 0.4)
|
|
659
|
+
if len(other_values) > 0:
|
|
660
|
+
self.__loadingAnimation(f"* Additional numerical values associated with the dimensions of the {obj_name} object is {' and '.join(str(v) for v in other_values)}", 0.4)
|
|
661
|
+
else:
|
|
662
|
+
self.__loadingAnimation(f"* No additional numerical values associated with the dimensions of the {obj_name} were given",0.4)
|
|
663
|
+
|
|
664
|
+
# Now iterate through the geometric identifier list, find the correct object, and then find its formula, then plug compute
|
|
665
|
+
formula = self.__geometric_calculation_identifiers[obj_name]["formula"]
|
|
666
|
+
params = self.__geometric_calculation_identifiers[obj_name]["params"]
|
|
667
|
+
|
|
668
|
+
formula_inputs = {} # all data gathered to compute geometry
|
|
669
|
+
|
|
670
|
+
try:
|
|
671
|
+
if "height" in params:
|
|
672
|
+
formula_inputs["height"] = height_value
|
|
673
|
+
|
|
674
|
+
value_idx = 0 # count how many values to be added in formula_inputs
|
|
675
|
+
for param in params:
|
|
676
|
+
if param == "height":
|
|
677
|
+
continue # already added
|
|
678
|
+
elif param == "other": # two consecutive numbers to append
|
|
679
|
+
formula_inputs["other"] = other_values[value_idx:value_idx + 2]
|
|
680
|
+
value_idx += 2
|
|
681
|
+
else: # only one number to append
|
|
682
|
+
formula_inputs[param] = other_values[value_idx]
|
|
683
|
+
value_idx += 1
|
|
684
|
+
|
|
685
|
+
# Try calculating the result and return
|
|
686
|
+
result = round(formula(formula_inputs), 4)
|
|
687
|
+
return result
|
|
688
|
+
|
|
689
|
+
except Exception as e:
|
|
690
|
+
if display_thought:
|
|
691
|
+
print(f"{'\033[33m'}Unable to compute the {object_intel[0]} of the {obj_name} due to missing or mismatched values{'\033[0m'}")
|
|
692
|
+
else:
|
|
693
|
+
print(f"{'\033[34m'}Unable to compute the {object_intel[0]} of the {obj_name} due to missing or mismatched values{'\033[0m'}")
|
|
694
|
+
return None
|
|
695
|
+
|
|
696
|
+
|
|
537
697
|
def __perform_advanced_CoT(self, filtered_query, display_thought): # no return, void
|
|
538
698
|
"""
|
|
539
699
|
Perform advanced Chain-of-Thought (CoT) reasoning to solve arithmetic or unit conversion problems.
|
|
@@ -570,8 +730,7 @@ class DLM:
|
|
|
570
730
|
print(
|
|
571
731
|
f"{'\033[33m'}I am presented with a more involved query asking me to do some form of computation{'\033[0m'}")
|
|
572
732
|
self.__loadingAnimation("Let me think about this carefully and break it down so that I can solve it", 0.8)
|
|
573
|
-
self.__loadingAnimation(f"I’ve trimmed away any extra words so I’m focusing on \"{filtered_query}\" now",
|
|
574
|
-
0.8)
|
|
733
|
+
self.__loadingAnimation(f"I’ve trimmed away any extra words so I’m focusing on \"{filtered_query}\" now", 0.8)
|
|
575
734
|
|
|
576
735
|
# Have the bot pick out names mentioned (in order) using SpaCy and NLTK (for maximum coverage)
|
|
577
736
|
for ent in doc.ents:
|
|
@@ -599,176 +758,189 @@ class DLM:
|
|
|
599
758
|
tokens_lower = filtered_query.lower().split()
|
|
600
759
|
last_two = set(tokens_lower[-2:]) # only the final 2 words from filtered input
|
|
601
760
|
|
|
602
|
-
#
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
761
|
+
# First see if the problem is a geometric problem
|
|
762
|
+
words = filtered_query.lower().split()
|
|
763
|
+
geometric_ans = None
|
|
764
|
+
# checks if the query contains shapes or object to perform possibly formula calculation
|
|
765
|
+
geometric_calc = any(difflib.get_close_matches(word, self.__geometric_calculation_identifiers.keys(), n=1, cutoff=0.70) for word in words)
|
|
766
|
+
is_geometric_query = False
|
|
767
|
+
|
|
768
|
+
geo_types = set() # currently supported types of geometric calculations
|
|
769
|
+
|
|
770
|
+
for t in self.__geometric_calculation_identifiers:
|
|
771
|
+
shape = self.__geometric_calculation_identifiers[t]["keywords"]
|
|
772
|
+
geo_types.add(shape[0])
|
|
773
|
+
if any(difflib.get_close_matches(word, geo_types, n=1, cutoff=0.70) for word in words) and geometric_calc:
|
|
774
|
+
geometric_ans = self.__geometric_calculation(filtered_query, display_thought)
|
|
775
|
+
if geometric_ans is not None:
|
|
776
|
+
is_geometric_query = True
|
|
777
|
+
else: # Not geometric, so have the bot find all operand indicating keywords
|
|
778
|
+
found_operand = False
|
|
779
|
+
for fq in filtered_query.split():
|
|
780
|
+
fq_l = fq.lower()
|
|
781
|
+
# If this word is one of the ending phrases and sits among the last five, skip it
|
|
782
|
+
if fq_l in arithmetic_ending_phrases and fq_l in last_two:
|
|
783
|
+
continue
|
|
784
|
+
if fq_l in {"+", "-", "*", "/"}:
|
|
785
|
+
operands_mentioned.append(fq_l)
|
|
786
|
+
keywords_mentioned.append(fq_l)
|
|
787
|
+
continue # move on to the next token
|
|
788
|
+
for operand, keywords in self.__computation_identifiers.items():
|
|
789
|
+
for kw in keywords:
|
|
790
|
+
p1 = self.__nlp(kw)
|
|
791
|
+
p2 = self.__nlp(fq_l)
|
|
792
|
+
word_num_surrounded = re.search(rf'\d+\s*{fq.lower()}\s*\d+', filtered_query.lower())
|
|
793
|
+
|
|
794
|
+
# Direct match or lemma match
|
|
795
|
+
if (kw.lower() == fq.lower()) or p1[0].lemma_ == p2[0].lemma_:
|
|
796
|
+
keywords_mentioned.append(kw.title())
|
|
797
|
+
if kw.lower() == "average":
|
|
798
|
+
operands_mentioned.append("+")
|
|
799
|
+
elif kw.lower() == "out of":
|
|
800
|
+
if word_num_surrounded:
|
|
801
|
+
operands_mentioned.append(operand)
|
|
802
|
+
found_operand = True
|
|
803
|
+
break # only break if 'out of' condition is satisfied
|
|
804
|
+
continue # skip adding 'out of' if not surrounded by numbers
|
|
805
|
+
else:
|
|
626
806
|
operands_mentioned.append(operand)
|
|
627
807
|
found_operand = True
|
|
628
|
-
break
|
|
629
|
-
continue # skip adding 'out of' if not surrounded by numbers
|
|
630
|
-
else:
|
|
631
|
-
operands_mentioned.append(operand)
|
|
632
|
-
found_operand = True
|
|
633
|
-
break
|
|
808
|
+
break
|
|
634
809
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
810
|
+
# Vector + string similarity
|
|
811
|
+
if p1.vector_norm != 0 and p2.vector_norm != 0 and (
|
|
812
|
+
p1.similarity(p2) > 0.80 and difflib.SequenceMatcher(None, kw, fq_l).ratio() > 0.40):
|
|
813
|
+
keywords_mentioned.append(kw.title())
|
|
814
|
+
if kw.lower() == "average":
|
|
815
|
+
operands_mentioned.append("+")
|
|
816
|
+
elif kw.lower() == "out of":
|
|
817
|
+
if word_num_surrounded:
|
|
818
|
+
operands_mentioned.append(operand)
|
|
819
|
+
found_operand = True
|
|
820
|
+
break
|
|
821
|
+
continue
|
|
822
|
+
else:
|
|
643
823
|
operands_mentioned.append(operand)
|
|
644
824
|
found_operand = True
|
|
645
825
|
break
|
|
646
|
-
continue
|
|
647
|
-
else:
|
|
648
|
-
operands_mentioned.append(operand)
|
|
649
|
-
found_operand = True
|
|
650
|
-
break
|
|
651
826
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
827
|
+
# Fallback: high string similarity
|
|
828
|
+
elif difflib.SequenceMatcher(None, kw, fq_l).ratio() > 0.80:
|
|
829
|
+
keywords_mentioned.append(kw.title())
|
|
830
|
+
if kw.lower() == "average":
|
|
831
|
+
operands_mentioned.append("+")
|
|
832
|
+
elif kw.lower() == "out of":
|
|
833
|
+
if word_num_surrounded:
|
|
834
|
+
operands_mentioned.append(operand)
|
|
835
|
+
found_operand = True
|
|
836
|
+
break
|
|
837
|
+
continue
|
|
838
|
+
else:
|
|
659
839
|
operands_mentioned.append(operand)
|
|
660
840
|
found_operand = True
|
|
661
841
|
break
|
|
662
|
-
continue
|
|
663
|
-
else:
|
|
664
|
-
operands_mentioned.append(operand)
|
|
665
|
-
found_operand = True
|
|
666
|
-
break
|
|
667
|
-
|
|
668
|
-
if found_operand:
|
|
669
|
-
found_operand = False
|
|
670
|
-
break
|
|
671
842
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
for fq in filtered_query.split():
|
|
675
|
-
p_fq = self.__nlp(fq)
|
|
676
|
-
|
|
677
|
-
# Replace exact matching with a spaCy similarity check against ending_phrases
|
|
678
|
-
matched_ep = None
|
|
679
|
-
for ep in arithmetic_ending_phrases:
|
|
680
|
-
p_ep = self.__nlp(ep)
|
|
681
|
-
if p_ep.vector_norm != 0 and p_fq.vector_norm != 0 and p_ep.similarity(p_fq) > 0.50:
|
|
682
|
-
matched_ep = ep
|
|
843
|
+
if found_operand:
|
|
844
|
+
found_operand = False
|
|
683
845
|
break
|
|
684
846
|
|
|
685
|
-
|
|
686
|
-
|
|
847
|
+
# If no operands were found in the main pass, check ending phrases as a last resort
|
|
848
|
+
if not operands_mentioned:
|
|
849
|
+
for fq in filtered_query.split():
|
|
850
|
+
p_fq = self.__nlp(fq)
|
|
851
|
+
|
|
852
|
+
# Replace exact matching with a spaCy similarity check against ending_phrases
|
|
853
|
+
matched_ep = None
|
|
854
|
+
for ep in arithmetic_ending_phrases:
|
|
855
|
+
p_ep = self.__nlp(ep)
|
|
856
|
+
if p_ep.vector_norm != 0 and p_fq.vector_norm != 0 and p_ep.similarity(p_fq) > 0.50:
|
|
857
|
+
matched_ep = ep
|
|
858
|
+
break
|
|
687
859
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
860
|
+
if not matched_ep:
|
|
861
|
+
continue
|
|
862
|
+
|
|
863
|
+
# Now matched_ep roughly corresponds to an ending phrase; find its operand via spaCy
|
|
864
|
+
for operand, keywords in self.__computation_identifiers.items():
|
|
865
|
+
for kw in keywords:
|
|
866
|
+
p_kw = self.__nlp(kw)
|
|
867
|
+
if p_kw.vector_norm != 0 and p_fq.vector_norm != 0 and p_kw.similarity(p_fq) > 0.70:
|
|
868
|
+
keywords_mentioned.append(kw.title())
|
|
869
|
+
operands_mentioned.append(operand)
|
|
870
|
+
break
|
|
871
|
+
if operands_mentioned:
|
|
695
872
|
break
|
|
696
873
|
if operands_mentioned:
|
|
697
874
|
break
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
num_mentioned.append(str(float(token)))
|
|
713
|
-
continue
|
|
714
|
-
|
|
715
|
-
# Check if it's a word-based number (e.g. 'three', 'double', 'a')
|
|
716
|
-
try:
|
|
717
|
-
num = w2n.word_to_num(token)
|
|
718
|
-
num_mentioned.append(str(float(num)))
|
|
719
|
-
continue
|
|
720
|
-
except ValueError:
|
|
721
|
-
pass # not a word2num-recognized word
|
|
722
|
-
|
|
723
|
-
# Check if it's in our custom list (a, an, half, double, etc.)
|
|
724
|
-
for t in text_nums:
|
|
725
|
-
p1 = self.__nlp(token)
|
|
726
|
-
p2 = self.__nlp(t)
|
|
727
|
-
if p1[0].lemma_ == p2[0].lemma_:
|
|
728
|
-
if t == "double":
|
|
729
|
-
num_mentioned.append(float(2).__str__())
|
|
730
|
-
elif t == "triple":
|
|
731
|
-
num_mentioned.append(float(3).__str__())
|
|
732
|
-
elif t == "half":
|
|
733
|
-
num_mentioned.append(float(0.5).__str__())
|
|
734
|
-
elif ("=" in operands_mentioned) and (t == "a" or t == "an"):
|
|
735
|
-
a_an_detected = True
|
|
736
|
-
num_mentioned.append(float(1.0).__str__())
|
|
737
|
-
elif t == "quadruple":
|
|
738
|
-
num_mentioned.append(float(4).__str__())
|
|
739
|
-
|
|
740
|
-
# Remove "1.0" if 'a'/'an' was used in an invalid context (like not following "=")
|
|
741
|
-
if a_an_detected and (num_mentioned.count("1.0") > 1 or len(num_mentioned) > 1):
|
|
742
|
-
num_mentioned.remove("1.0")
|
|
743
|
-
|
|
744
|
-
if ('=' in operands_mentioned) and (len(num_mentioned) < 2):
|
|
745
|
-
operands_mentioned.clear()
|
|
746
|
-
operands_mentioned.append('=')
|
|
747
|
-
else:
|
|
748
|
-
if '=' in operands_mentioned:
|
|
749
|
-
operands_mentioned = [op for op in operands_mentioned if op != '=']
|
|
875
|
+
keywords_mentioned = list(dict.fromkeys(keywords_mentioned))
|
|
876
|
+
|
|
877
|
+
# Now have the bot pick out numbers (in order)
|
|
878
|
+
# additionally, "double", "triple", "quadruple", "half", "an" and "a" also count as numbers, in addition to text numbers (e.g. "three")
|
|
879
|
+
text_nums = ["a", "an", "half", "double", "triple", "quadruple"]
|
|
880
|
+
a_an_detected = False
|
|
881
|
+
|
|
882
|
+
# Combined regex and word match pass
|
|
883
|
+
tokens = filtered_query.lower().split()
|
|
884
|
+
for token in tokens:
|
|
885
|
+
# Check if it's a digit-based number (e.g. 600, 20.5)
|
|
886
|
+
if re.fullmatch(r"\d+(\.\d+)?", token):
|
|
887
|
+
num_mentioned.append(str(float(token)))
|
|
888
|
+
continue
|
|
750
889
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
890
|
+
# Check if it's a word-based number (e.g. 'three', 'double', 'a')
|
|
891
|
+
try:
|
|
892
|
+
num = w2n.word_to_num(token)
|
|
893
|
+
num_mentioned.append(str(float(num)))
|
|
894
|
+
continue
|
|
895
|
+
except ValueError:
|
|
896
|
+
pass # not a word2num-recognized word
|
|
897
|
+
|
|
898
|
+
# Check if it's in our custom list (a, an, half, double, etc.)
|
|
899
|
+
for t in text_nums:
|
|
900
|
+
p1 = self.__nlp(token)
|
|
901
|
+
p2 = self.__nlp(t)
|
|
902
|
+
if p1[0].lemma_ == p2[0].lemma_:
|
|
903
|
+
if t == "double":
|
|
904
|
+
num_mentioned.append(float(2).__str__())
|
|
905
|
+
elif t == "triple":
|
|
906
|
+
num_mentioned.append(float(3).__str__())
|
|
907
|
+
elif t == "half":
|
|
908
|
+
num_mentioned.append(float(0.5).__str__())
|
|
909
|
+
elif ("=" in operands_mentioned) and (t == "a" or t == "an"):
|
|
910
|
+
a_an_detected = True
|
|
911
|
+
num_mentioned.append(float(1.0).__str__())
|
|
912
|
+
elif t == "quadruple":
|
|
913
|
+
num_mentioned.append(float(4).__str__())
|
|
914
|
+
|
|
915
|
+
# Remove "1.0" if 'a'/'an' was used in an invalid context (like not following "=")
|
|
916
|
+
if a_an_detected and (num_mentioned.count("1.0") > 1 or len(num_mentioned) > 1):
|
|
917
|
+
num_mentioned.remove("1.0")
|
|
918
|
+
|
|
919
|
+
if ('=' in operands_mentioned) and (len(num_mentioned) < 2):
|
|
920
|
+
operands_mentioned.clear()
|
|
921
|
+
operands_mentioned.append('=')
|
|
922
|
+
else:
|
|
923
|
+
if '=' in operands_mentioned:
|
|
924
|
+
operands_mentioned = [op for op in operands_mentioned if op != '=']
|
|
925
|
+
|
|
926
|
+
# verify and possibly print thoughts
|
|
927
|
+
if (not is_geometric_query) and (any(not lst for lst in (num_mentioned, operands_mentioned)) or ('=' not in operands_mentioned and num_mentioned.__len__() < 2)): # don't compute if parts are missing
|
|
928
|
+
print(f"{self.__loadingAnimation('Hmm', 0.8) or '' if display_thought else ''}{'\033[34m'}It looks like some essential details are missing, so I can’t complete this calculation right now.{'\033[0m'}")
|
|
929
|
+
print(f"\033[34mIf you are asking a geometric query, try including geometric identifiers like \"{'\", \"'.join(geo_types)}\" in your query.\033[0m")
|
|
930
|
+
print(f"\033[34mCurrently, I can only compute those identifiers aforementioned, but more geometric features are coming soon!\033[0m")
|
|
756
931
|
else: # else, the bot needs to explain what it has tokenized
|
|
757
932
|
if display_thought:
|
|
758
|
-
self.__loadingAnimation(
|
|
759
|
-
f"1.) I see {', '.join(persons_mentioned) if persons_mentioned.__len__() >= 1 else 'no one'} mentioned as a person name; "
|
|
933
|
+
self.__loadingAnimation(f"1.) I see {', '.join(persons_mentioned) if persons_mentioned.__len__() >= 1 else 'no one'} mentioned as a person name; "
|
|
760
934
|
f"{'they’re likely key to this problem' if persons_mentioned.__len__() >= 1 else 'moving on'}", 0.2)
|
|
761
|
-
self.__loadingAnimation(
|
|
762
|
-
f"2.) Moreover, I see {', '.join(items_mentioned) if items_mentioned.__len__() >= 1 else 'no items'} mentioned as proper nouns; "
|
|
935
|
+
self.__loadingAnimation(f"2.) Moreover, I see {', '.join(items_mentioned) if items_mentioned.__len__() >= 1 else 'no items'} mentioned as proper nouns; "
|
|
763
936
|
f"{'this might be a key thing to this problem' if items_mentioned.__len__() >= 1 else 'moving on'}",
|
|
764
937
|
0.2)
|
|
765
|
-
|
|
766
|
-
f"3.)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
f"4.) I see the keywords \"{'\" and \"'.join(keywords_mentioned)}\", meaning I need to perform a \"{'\" and \"'.join(operands_mentioned)}\" operation for this query; I’ll use that to guide my calculation",
|
|
770
|
-
0.
|
|
771
|
-
self.__loadingAnimation("Now I have the parts, so let me put it all together and solve", 0.3)
|
|
938
|
+
if is_geometric_query:
|
|
939
|
+
self.__loadingAnimation(f"3.) This is a geometric problem and I have already computed the answer", 0.2)
|
|
940
|
+
else:
|
|
941
|
+
self.__loadingAnimation(f"3.) I’ve also identified the numbers {' and '.join(num_mentioned)} that I need to compute with", 0.2)
|
|
942
|
+
self.__loadingAnimation(f"4.) I see the keywords \"{'\" and \"'.join(keywords_mentioned)}\", meaning I need to perform a \"{'\" and \"'.join(operands_mentioned)}\" operation for this query; I’ll use that to guide my calculation", 0.2)
|
|
943
|
+
self.__loadingAnimation("Now I have the parts, so let me put it all together and solve", 0.3)
|
|
772
944
|
|
|
773
945
|
# Finally compute it and then give the response (if there is any)
|
|
774
946
|
# move "originally" numbers to the front
|
|
@@ -801,8 +973,11 @@ class DLM:
|
|
|
801
973
|
num_mentioned.remove(str(float(temp)))
|
|
802
974
|
num_mentioned.insert(0, str(float(temp)))
|
|
803
975
|
|
|
976
|
+
# geometric problem
|
|
977
|
+
if is_geometric_query:
|
|
978
|
+
print(f"{'\033[34m'}Geometric Answer: {geometric_ans}{'\033[0m'}")
|
|
804
979
|
# conversion problem
|
|
805
|
-
|
|
980
|
+
elif len(num_mentioned) == 1 and len(operands_mentioned) == 1:
|
|
806
981
|
try:
|
|
807
982
|
tokens = filtered_query.lower().split()
|
|
808
983
|
num0 = float(num_mentioned[0])
|
{dynamic_learning_model-1.2.6 → dynamic_learning_model-1.3/dynamic_learning_model.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dynamic-learning-model
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3
|
|
4
4
|
Summary: A Dynamic Learning Model for processing NLP queries using hybrid AI and reasoning.
|
|
5
5
|
Home-page: https://github.com/VigneshT24/Dynamic_Learning_Model
|
|
6
6
|
Author: Vignesh Thondikulam
|
|
@@ -5,7 +5,7 @@ with open('README.md', 'r', encoding='utf-8') as f:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name='dynamic-learning-model',
|
|
8
|
-
version='1.
|
|
8
|
+
version='1.3',
|
|
9
9
|
author='Vignesh Thondikulam',
|
|
10
10
|
author_email='vignesh.tho2006@gmail.com',
|
|
11
11
|
description='A Dynamic Learning Model for processing NLP queries using hybrid AI and reasoning.',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|