vector-vault 4.2.4__tar.gz → 4.2.6__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.
Files changed (21) hide show
  1. {vector_vault-4.2.4 → vector_vault-4.2.6}/PKG-INFO +1 -1
  2. {vector_vault-4.2.4 → vector_vault-4.2.6}/setup.py +1 -1
  3. {vector_vault-4.2.4 → vector_vault-4.2.6}/vector_vault.egg-info/PKG-INFO +1 -1
  4. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/cloudmanager.py +8 -17
  5. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/itemize.py +25 -1
  6. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/tools_gpt.py +1 -1
  7. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/vault.py +146 -48
  8. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/vecreq.py +28 -0
  9. {vector_vault-4.2.4 → vector_vault-4.2.6}/LICENSE +0 -0
  10. {vector_vault-4.2.4 → vector_vault-4.2.6}/README.md +0 -0
  11. {vector_vault-4.2.4 → vector_vault-4.2.6}/setup.cfg +0 -0
  12. {vector_vault-4.2.4 → vector_vault-4.2.6}/vector_vault.egg-info/SOURCES.txt +0 -0
  13. {vector_vault-4.2.4 → vector_vault-4.2.6}/vector_vault.egg-info/dependency_links.txt +0 -0
  14. {vector_vault-4.2.4 → vector_vault-4.2.6}/vector_vault.egg-info/requires.txt +0 -0
  15. {vector_vault-4.2.4 → vector_vault-4.2.6}/vector_vault.egg-info/top_level.txt +0 -0
  16. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/__init__.py +0 -0
  17. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/ai.py +0 -0
  18. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/cloud_api.py +0 -0
  19. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/creds.py +0 -0
  20. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/download.py +0 -0
  21. {vector_vault-4.2.4 → vector_vault-4.2.6}/vectorvault/wrap.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vector_vault
3
- Version: 4.2.4
3
+ Version: 4.2.6
4
4
  Summary: Quickly create ChatGPT RAG apps and Unleash the full potential of GenAI with Vector Vault
5
5
  Home-page: https://github.com/John-Rood/VectorVault
6
6
  Author: VectorVault.io
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="vector_vault",
5
- version="4.2.4",
5
+ version="4.2.6",
6
6
  packages=find_packages(),
7
7
  author="VectorVault.io",
8
8
  author_email="john@johnrood.com",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vector-vault
3
- Version: 4.2.4
3
+ Version: 4.2.6
4
4
  Summary: Quickly create ChatGPT RAG apps and Unleash the full potential of GenAI with Vector Vault
5
5
  Home-page: https://github.com/John-Rood/VectorVault
6
6
  Author: VectorVault.io
@@ -17,13 +17,14 @@ from google.cloud import storage
17
17
  from .creds import CustomCredentials
18
18
  from .vecreq import call_proj, call_update
19
19
  from .itemize import cloud_name
20
- from .vault import ThreadPoolExecutor, as_completed, T as t, time, json, os, tempfile
20
+ from .vault import ThreadPoolExecutor, as_completed, time, json, os, tempfile, time
21
21
 
22
22
  class CloudManager:
23
- def __init__(self, user: str, api_key: str, vault: str):
23
+ def __init__(self, user: str, api_key: str, vault: str, ai_api: str = None):
24
24
  self.user = user
25
25
  self.api = api_key
26
26
  self.vault = vault
27
+ self.ai_api = ai_api
27
28
  # Create credentials
28
29
  self.credentials = CustomCredentials(user, self.api)
29
30
  # Instantiate the client
@@ -84,19 +85,10 @@ class CloudManager:
84
85
  os.close(temp_file_descriptor)
85
86
  return temp_file_path
86
87
 
87
- def upload(self, item, text, meta):
88
- self.upload_to_cloud(self.cloud_name(self.vault, item, self.user, self.api, item=True), text)
89
- self.upload_to_cloud(self.cloud_name(self.vault, item, self.user, self.api, meta=True), json.dumps(meta))
90
-
91
- def upload_personality_message(self, personality_message):
92
- self.upload_to_cloud(f'{self.vault}/personality_message', personality_message)
93
-
94
- def upload_custom_prompt(self, prompt):
95
- self.upload_to_cloud(f'{self.vault}/prompt', prompt)
96
-
97
- def upload_no_context_prompt(self, prompt):
98
- self.upload_to_cloud(f'{self.vault}/no_context_prompt', prompt)
99
-
88
+ def upload(self, item, text, meta, vault = None):
89
+ self.upload_to_cloud(self.cloud_name(vault if vault else self.vault, item, self.user, self.api, item=True), text)
90
+ self.upload_to_cloud(self.cloud_name(vault if vault else self.vault, item, self.user, self.api, meta=True), json.dumps(meta))
91
+
100
92
  def username(self, input_string):
101
93
  return input_string.replace("@", "_at_").replace(".", "_dot_") + '_vvclient'
102
94
 
@@ -108,8 +100,7 @@ class CloudManager:
108
100
  return _map
109
101
 
110
102
  def update(self):
111
- th = t(target=call_update, args=(self.user, self.vault, self.api))
112
- th.start()
103
+ call_update(self.user, self.vault, self.api)
113
104
 
114
105
  def build_update(self):
115
106
  _map = self.get_mapping()
@@ -91,4 +91,28 @@ def build_return(item_data, meta, distance=None):
91
91
  "metadata": meta,
92
92
  "distance": distance
93
93
  }
94
- return result
94
+ return result
95
+
96
+ def get_time_statement(now, message_time):
97
+ diff = now - message_time
98
+ days, seconds = diff.days, diff.seconds
99
+ human_readable_time = ""
100
+
101
+ if days >= 365:
102
+ years = days // 365
103
+ human_readable_time = f"{years} {'year' if years == 1 else 'years'} ago: "
104
+ elif days >= 30:
105
+ months = days // 30
106
+ human_readable_time = f"{months} {'month' if months == 1 else 'months'} ago: "
107
+ elif days >= 1:
108
+ human_readable_time = f"{days} {'day' if days == 1 else 'days'} ago: "
109
+ elif seconds >= 3600:
110
+ hours = seconds // 3600
111
+ human_readable_time = f"{hours} {'hour' if hours == 1 else 'hours'} ago: "
112
+ elif seconds >= 60:
113
+ minutes = seconds // 60
114
+ human_readable_time = f"{minutes} {'minute' if minutes == 1 else 'minutes'} ago: "
115
+ else:
116
+ human_readable_time = "just now: "
117
+
118
+ return human_readable_time
@@ -1,4 +1,4 @@
1
- from .ai import AI
1
+ from ai import AI
2
2
  import time
3
3
  import ast
4
4
 
@@ -23,19 +23,19 @@ import re
23
23
  import json
24
24
  import traceback
25
25
  import random
26
- from datetime import datetime
27
26
  from typing import List
28
- from concurrent.futures import ThreadPoolExecutor, as_completed
27
+ from ai import AI, openai
29
28
  from threading import Thread as T
29
+ from datetime import datetime, timedelta
30
+ from .concurrent.futures import ThreadPoolExecutor, as_completed
30
31
  from .cloudmanager import CloudManager
31
- from .ai import AI, openai
32
- from .itemize import itemize, name_vecs, get_item, get_vectors, build_return, cloud_name, name_map
33
- from .vecreq import call_get_similar
32
+ from .itemize import itemize, name_vecs, get_item, get_vectors, build_return, cloud_name, name_map, get_time_statement
33
+ from .vecreq import call_get_similar, call_cloud_save
34
34
  from .tools_gpt import ToolsGPT
35
35
 
36
36
 
37
37
  class Vault:
38
- def __init__(self, user: str = None, api_key: str = None, vault: str = None, openai_key: str = None, embeddings_model: str = None, verbose: bool = False):
38
+ def __init__(self, user: str = None, api_key: str = None, openai_key: str = None, vault: str = None, embeddings_model: str = None, verbose: bool = False, conversation_user_id: str = None):
39
39
  '''
40
40
  ### Create a vector database instance like this:
41
41
  ```
@@ -75,11 +75,11 @@ class Vault:
75
75
  self.user = user.lower()
76
76
  self.vault = vault.strip() if vault else 'home'
77
77
  self.api = api_key
78
- t = T(target=self.connect_to_cloud)
79
- t.start()
80
78
  if openai_key:
81
79
  self.openai_key = openai_key
82
80
  openai.api_key = self.openai_key
81
+ t = T(target=self.connect_to_cloud)
82
+ t.start()
83
83
  self.embeddings_model = embeddings_model if embeddings_model else 'text-embedding-3-small'
84
84
  self.dims = 1536 if embeddings_model != 'text-embedding-3-large' else 3072
85
85
  self.vectors = get_vectors(self.dims)
@@ -94,12 +94,13 @@ class Vault:
94
94
  self.ai_loaded = False
95
95
  self.tools = ToolsGPT(verbose=verbose)
96
96
  self.rate_limiter = RateLimiter(max_attempts=30)
97
+ self.cuid = conversation_user_id
97
98
  t.join()
98
99
 
99
100
 
100
101
  def connect_to_cloud(self):
101
102
  try:
102
- self.cloud_manager = CloudManager(self.user, self.api, self.vault)
103
+ self.cloud_manager = CloudManager(self.user, self.api, self.vault, self.openai_key)
103
104
  print(f'Connected vault: {self.vault}') if self.verbose else 0
104
105
 
105
106
  except Exception as e:
@@ -113,6 +114,8 @@ class Vault:
113
114
  Returns a list of vaults within the current vault directory
114
115
  '''
115
116
  vault = self.vault if vault is None else vault
117
+ if self.cloud_manager is None:
118
+ time.sleep(.3)
116
119
  return self.cloud_manager.list_vaults(vault)
117
120
 
118
121
 
@@ -218,7 +221,7 @@ class Vault:
218
221
  return prompt
219
222
 
220
223
 
221
- def save(self, trees: int = 10):
224
+ def save(self, trees: int = 10, vault = None):
222
225
  '''
223
226
  Saves all the data added locally to the Cloud. All Vault references are Cloud references.
224
227
  To add data to your Vault and access it later, you must first call add(), then get_vectors(), and finally save().
@@ -235,10 +238,10 @@ class Vault:
235
238
  with ThreadPoolExecutor() as executor:
236
239
  for item in self.items:
237
240
  item_text, item_id, item_meta = get_item(item)
238
- executor.submit(self.cloud_manager.upload, self.map.get(str(item_id)), item_text, item_meta)
241
+ executor.submit(self.cloud_manager.upload, self.map.get(str(item_id)), item_text, item_meta, vault = vault if vault else self.vault)
239
242
  total_saved_items += 1
240
243
 
241
- self.upload_vectors()
244
+ self.upload_vectors(vault if vault else self.vault)
242
245
  print(f"upload time --- {(time.time() - start_time)} seconds --- {total_saved_items} items saved") if self.verbose else 0
243
246
 
244
247
 
@@ -389,6 +392,8 @@ class Vault:
389
392
  def check_index(self):
390
393
  if not self.x_checked:
391
394
  start_time = time.time()
395
+ while self.cloud_manager is None:
396
+ time.sleep(.01)
392
397
  if self.cloud_manager.vault_exists(name_vecs(self.vault, self.user, self.api)):
393
398
  if not self.vecs_loaded:
394
399
  self.load_vectors()
@@ -398,15 +403,16 @@ class Vault:
398
403
  print("initialize index --- %s seconds ---" % (time.time() - start_time)) if self.verbose else 0
399
404
 
400
405
 
401
- def load_mapping(self):
406
+ def load_mapping(self, vault = None):
402
407
  '''Internal function only'''
408
+ vault = vault if vault else self.vault
403
409
  try: # try to get the map
404
- temp_file_path = self.cloud_manager.download_to_temp_file(name_map(self.vault, self.user, self.api))
410
+ temp_file_path = self.cloud_manager.download_to_temp_file(name_map(vault, self.user, self.api))
405
411
  with open(temp_file_path, 'r') as json_file:
406
412
  self.map = json.load(json_file)
407
413
  os.remove(temp_file_path)
408
414
  except: # it doesn't exist
409
- if self.cloud_manager.vault_exists(name_vecs(self.vault, self.user, self.api)): # but if the vault does
415
+ if self.cloud_manager.vault_exists(name_vecs(vault, self.user, self.api)): # but if the vault does
410
416
  self.map = {str(i): str(i) for i in range(self.vectors.get_n_items())}
411
417
 
412
418
  def add_to_map(self):
@@ -414,11 +420,12 @@ class Vault:
414
420
  self.x +=1
415
421
 
416
422
 
417
- def load_vectors(self):
423
+ def load_vectors(self, vault = None):
418
424
  start_time = time.time()
425
+ vault = vault if vault else self.vault
419
426
  t = T(target=self.load_mapping())
420
427
  t.start()
421
- temp_file_path = self.cloud_manager.download_to_temp_file(name_vecs(self.vault, self.user, self.api))
428
+ temp_file_path = self.cloud_manager.download_to_temp_file(name_vecs(vault, self.user, self.api))
422
429
  self.vectors.load(temp_file_path)
423
430
  os.remove(temp_file_path)
424
431
  t.join()
@@ -535,19 +542,20 @@ class Vault:
535
542
  self.vectors = new_index
536
543
 
537
544
 
538
- def upload_vectors(self):
545
+ def upload_vectors(self, vault = None):
546
+ vault = vault if vault else self.vault
539
547
  with tempfile.NamedTemporaryFile(delete=False) as temp_file:
540
548
  vector_temp_file_path = temp_file.name
541
549
  self.vectors.save(vector_temp_file_path)
542
550
  byte = os.path.getsize(vector_temp_file_path)
543
- self.cloud_manager.upload_temp_file(vector_temp_file_path, name_vecs(self.vault, self.user, self.api, byte))
551
+ self.cloud_manager.upload_temp_file(vector_temp_file_path, name_vecs(vault, self.user, self.api, byte))
544
552
 
545
553
  map_temp_file_path = None
546
554
  with tempfile.NamedTemporaryFile(mode='w', delete=False) as temp_file:
547
555
  map_temp_file_path = temp_file.name
548
556
  json.dump(self.map, temp_file, indent=2)
549
557
 
550
- self.cloud_manager.upload_temp_file(map_temp_file_path, name_map(self.vault, self.user, self.api, byte))
558
+ self.cloud_manager.upload_temp_file(map_temp_file_path, name_map(vault, self.user, self.api, byte))
551
559
  if os.path.exists(map_temp_file_path):
552
560
  os.remove(map_temp_file_path)
553
561
 
@@ -662,12 +670,13 @@ class Vault:
662
670
  return results
663
671
 
664
672
 
665
- def get_items_by_vector(self, vector: list, n: int = 4, include_distances: bool = False):
673
+ def get_items_by_vector(self, vector: list, n: int = 4, include_distances: bool = False, vault = None) -> list:
666
674
  '''
667
675
  Internal function that returns vector similar items. Requires input vector, returns similar items
668
676
  '''
669
677
  try:
670
- self.load_vectors()
678
+ vault = vault if vault else self.vault
679
+ self.load_vectors(vault)
671
680
  start_time = time.time()
672
681
  if not include_distances:
673
682
  vecs = self.vectors.get_nns_by_vector(vector, n)
@@ -675,8 +684,8 @@ class Vault:
675
684
 
676
685
  def fetch_item(index, vec):
677
686
  # Function to fetch a single item, to be run in parallel
678
- item_data = self.cloud_manager.download_text_from_cloud(cloud_name(self.vault, self.map[str(vec)], self.user, self.api, item=True))
679
- meta_data = self.cloud_manager.download_text_from_cloud(cloud_name(self.vault, self.map[str(vec)], self.user, self.api, meta=True))
687
+ item_data = self.cloud_manager.download_text_from_cloud(cloud_name(vault, self.map[str(vec)], self.user, self.api, item=True))
688
+ meta_data = self.cloud_manager.download_text_from_cloud(cloud_name(vault, self.map[str(vec)], self.user, self.api, meta=True))
680
689
  meta = json.loads(meta_data)
681
690
  return index, build_return(item_data, meta)
682
691
 
@@ -695,8 +704,8 @@ class Vault:
695
704
 
696
705
  def fetch_item(index, vec, distance):
697
706
  # Function to fetch a single item, to be run in parallel
698
- item_data = self.cloud_manager.download_text_from_cloud(cloud_name(self.vault, self.map[str(vec)], self.user, self.api, item=True))
699
- meta_data = self.cloud_manager.download_text_from_cloud(cloud_name(self.vault, self.map[str(vec)], self.user, self.api, meta=True))
707
+ item_data = self.cloud_manager.download_text_from_cloud(cloud_name(vault, self.map[str(vec)], self.user, self.api, item=True))
708
+ meta_data = self.cloud_manager.download_text_from_cloud(cloud_name(vault, self.map[str(vec)], self.user, self.api, meta=True))
700
709
  meta = json.loads(meta_data)
701
710
  return index, build_return(item_data, meta, distance)
702
711
 
@@ -713,17 +722,17 @@ class Vault:
713
722
  return [{'data': 'No data has been added', 'metadata': {'no meta': 'No metadata has been added'}}]
714
723
 
715
724
 
716
- def get_similar_local(self, text: str, n: int = 4, include_distances: bool = False):
725
+ def get_similar_local(self, text: str, n: int = 4, include_distances: bool = False, vault = None):
717
726
  '''
718
727
  Returns similar items from the Vault as the one you entered, but locally
719
728
  (saves a few milliseconds and is sometimes used on production builds)
720
729
  '''
721
730
  self.cloud_manager.update()
722
731
  vector = self.process_batch([text], never_stop=False, loop_timeout=180)[0]
723
- return self.get_items_by_vector(vector, n, include_distances=include_distances)
732
+ return self.get_items_by_vector(vector, n, include_distances = include_distances, vault = vault if vault else self.vault)
724
733
 
725
734
 
726
- def get_similar(self, text: str, n: int = 4, include_distances: bool = False):
735
+ def get_similar(self, text: str, n: int = 4, include_distances: bool = False, vault = None):
727
736
  '''
728
737
  Returns similar items from the Vault as the text you enter.
729
738
 
@@ -731,21 +740,21 @@ class Vault:
731
740
  The distance can be useful for assessing similarity differences in the items returned.
732
741
  Each item has its' own distance number, and this changes the structure of the output.
733
742
  '''
734
- return call_get_similar(self.user, self.vault, self.api, self.openai_key, text, n, include_distances=include_distances, verbose=self.verbose)
743
+ return call_get_similar(self.user, vault if vault else self.vault, self.api, self.openai_key, text, n, include_distances=include_distances, verbose=self.verbose)
735
744
 
736
745
 
737
- def add_item(self, text: str, meta: dict = None, name: str = None):
746
+ def add_item(self, text: str, meta: dict = None, name: str = None, vault = None):
738
747
  """
739
748
  If your text length is greater than 15000 characters, you should use Vault.split_text(your_text) to
740
749
  get a list of text segments that are the right size
741
750
  """
742
751
  self.check_index()
743
- new_item = itemize(self.vault, self.x, meta, text, name)
752
+ new_item = itemize(vault if vault else self.vault, self.x, meta, text, name)
744
753
  self.items.append(new_item)
745
754
  self.add_to_map()
746
755
 
747
756
 
748
- def add(self, text: str, meta: dict = None, name: str = None, split: bool = False, split_size: int = 1000, max_threshold: int = 16000):
757
+ def add(self, text: str, meta: dict = None, name: str = None, split: bool = False, split_size: int = 1000, max_threshold: int = 16000, vault = None):
749
758
  """
750
759
  If your text length is greater than 4000 tokens, Vault.split_text(your_text)
751
760
  will automatically be added
@@ -759,18 +768,19 @@ class Vault:
759
768
  else:
760
769
  texts = [text]
761
770
  for text in texts:
762
- self.add_item(text, meta, name)
771
+ self.add_item(text, meta, name, vault if vault else self.vault)
763
772
 
764
773
 
765
- def add_n_save(self, text: str, meta: dict = None, name: str = None, split: bool = False, split_size: int = 1000, max_threshold: int = 16000):
774
+ def add_n_save(self, text: str, meta: dict = None, name: str = None, split: bool = False, split_size: int = 1000, max_threshold: int = 16000, vault = None):
766
775
  """
767
776
  Adds data, gets vectors, then saves the data to the cloud in one call
768
777
  If your text length is greater than 4000 tokens, your text will automatically be split by
769
778
  Vault.split_text(your_text).
770
779
  """
771
- self.add(text=text, meta=meta, name=name, split=split, split_size=split_size, max_threshold=max_threshold)
780
+ self.add(text=text, meta=meta, name=name, split=split, split_size=split_size, max_threshold=max_threshold, vault=vault if vault else self.vault)
772
781
  self.get_vectors()
773
- self.save()
782
+ self.save(vault = vault if vault else self.vault)
783
+ T(target=self.clear_cache).start()
774
784
 
775
785
 
776
786
  def add_item_with_vector(self, text: str, vector: list, meta: dict = None, name: str = None):
@@ -851,7 +861,7 @@ class Vault:
851
861
  print("get vectors time --- %s seconds ---" % (time.time() - start_time)) if self.verbose else 0
852
862
 
853
863
 
854
- def get_chat(self, text: str = None, history: str = None, summary: bool = False, get_context: bool = False,
864
+ def get_chat(self, text: str = None, history: str = '', summary: bool = False, get_context: bool = False,
855
865
  n_context: int = 4, return_context: bool = False, history_search: bool = False, smart_history_search: bool = False,
856
866
  model: str = 'gpt-3.5-turbo', include_context_meta: bool = False, custom_prompt: bool = False,
857
867
  local: bool =False, temperature: int = 0, timeout: int = 300):
@@ -936,10 +946,11 @@ class Vault:
936
946
  self.load_ai()
937
947
  model = model.lower()
938
948
  start_time = time.time()
939
-
940
- if not history:
941
- history = ''
942
-
949
+
950
+ history_time = time.time() if self.cuid else None
951
+ history = self.get_conversation_history(self.cuid, text) if self.cuid else history
952
+ print('history retrieval took:', history_time - time.time()) if self.cuid and self.verbose else None
953
+
943
954
  if text:
944
955
  inputs = [text]
945
956
  else:
@@ -1004,12 +1015,13 @@ class Vault:
1004
1015
  print(f"API Failed too many times, exiting loop: {e}.")
1005
1016
  break
1006
1017
 
1018
+ self.update_conversation_history(self.cuid, response) if self.cuid else None
1007
1019
  print("get chat time --- %s seconds ---" % (time.time() - start_time)) if self.verbose else 0
1008
1020
 
1009
1021
  return {'response': response, 'context': context} if return_context else response
1010
1022
 
1011
1023
 
1012
- def get_chat_stream(self, text: str = None, history: str = None, summary: bool = False, get_context: bool = False,
1024
+ def get_chat_stream(self, text: str = None, history: str = '', summary: bool = False, get_context: bool = False,
1013
1025
  n_context: int = 4, return_context: bool = False, history_search: bool = False, smart_history_search: bool = False,
1014
1026
  model: str ='gpt-3.5-turbo', include_context_meta: bool = False, metatag: bool = False,
1015
1027
  metatag_prefixes: bool = False, metatag_suffixes: bool = False, custom_prompt: bool = False,
@@ -1082,9 +1094,10 @@ class Vault:
1082
1094
  self.load_ai()
1083
1095
  model = model.lower()
1084
1096
  start_time = time.time()
1085
-
1086
- if not history:
1087
- history = ''
1097
+
1098
+ history_time = time.time()
1099
+ history = self.get_conversation_history(self.cuid, text) if self.cuid else history
1100
+ print('history retrieval took:', history_time - time.time()) if self.cuid and self.verbose else None
1088
1101
 
1089
1102
  if text:
1090
1103
  inputs = [text]
@@ -1098,7 +1111,7 @@ class Vault:
1098
1111
  for segment in inputs:
1099
1112
  start_time = time.time()
1100
1113
  exceptions = 0
1101
-
1114
+ full_response = ''
1102
1115
  while True:
1103
1116
  try:
1104
1117
  if summary and not get_context:
@@ -1114,6 +1127,7 @@ class Vault:
1114
1127
  current_chunk = segment[start_index:end_index]
1115
1128
  # Process the current chunk and concatenate the response
1116
1129
  for word in self.ai.summarize_stream(current_chunk, model=model, custom_prompt=custom_prompt, temperature=temperature):
1130
+ full_response += word
1117
1131
  yield word
1118
1132
  yield ' '
1119
1133
 
@@ -1121,6 +1135,7 @@ class Vault:
1121
1135
  except Exception as e:
1122
1136
  raise e
1123
1137
  if counter == len(inputs):
1138
+ self.update_conversation_history(self.cuid, full_response) if self.cuid else None
1124
1139
  yield '!END'
1125
1140
  self.rate_limiter.on_success()
1126
1141
 
@@ -1138,6 +1153,7 @@ class Vault:
1138
1153
 
1139
1154
  try:
1140
1155
  for word in self.ai.llm_w_context_stream(segment, input_, history, model=model, custom_prompt=custom_prompt, temperature=temperature):
1156
+ full_response += word
1141
1157
  yield word
1142
1158
  self.rate_limiter.on_success()
1143
1159
  except Exception as e:
@@ -1157,20 +1173,25 @@ class Vault:
1157
1173
  for i in range(len(metatag)):
1158
1174
  yield str(metatag_prefixes[i]) + str(item['metadata'][f'{metatag[i]}'])
1159
1175
  yield item['data']
1176
+ self.update_conversation_history(self.cuid, full_response) if self.cuid else None
1160
1177
  yield '!END'
1161
1178
  self.rate_limiter.on_success()
1162
1179
  else:
1180
+ self.update_conversation_history(self.cuid, full_response) if self.cuid else None
1163
1181
  yield '!END'
1164
1182
  self.rate_limiter.on_success()
1165
1183
 
1166
1184
  else:
1167
1185
  try:
1168
1186
  for word in self.ai.llm_stream(segment, history, model=model, custom_prompt=custom_prompt, temperature=temperature):
1187
+ full_response += word
1169
1188
  yield word
1170
1189
  self.rate_limiter.on_success()
1171
1190
  except Exception as e:
1172
1191
  raise e
1192
+ self.update_conversation_history(self.cuid, full_response) if self.cuid else None
1173
1193
  yield '!END'
1194
+
1174
1195
  self.rate_limiter.on_success()
1175
1196
  break
1176
1197
  except Exception as e:
@@ -1250,6 +1271,83 @@ class Vault:
1250
1271
  yield f"data: {json.dumps({'data': word})} \n\n"
1251
1272
 
1252
1273
 
1274
+ def update_conversation_history(self, conversation_id, message, metadata_list = None, user = True):
1275
+ message = f'User: {message}' if user else f'AI: {message}'
1276
+ try:
1277
+ metadata_list = metadata_list if metadata_list else json.loads(self.cloud_manager.download_text_from_cloud(f'user_history/{conversation_id}/metadata'))
1278
+ except:
1279
+ metadata_list = []
1280
+ message_id = f"{time.time():.0f}" # Current time
1281
+ metadata_list.append({ 'M': message_id, 'L': len(message) })
1282
+ t1 = T(target=self.cloud_manager.upload_to_cloud, args=(f'user_history/{conversation_id}/{message_id}', message))
1283
+ t2 = T(target=self.cloud_manager.upload_to_cloud, args=(f'user_history/{conversation_id}/metadata', json.dumps(metadata_list)))
1284
+ t1.start()
1285
+ t2.start()
1286
+ call_cloud_save(self.user, self.api, self.openai_key, f'user_history/{conversation_id}/vectors', self.embeddings_model, message, name=message_id, split_size=3000)
1287
+ t1.join()
1288
+ t2.join()
1289
+
1290
+ def get_conversation_history(self, conversation_id, message):
1291
+ def download_conversation_metadata():
1292
+ try:
1293
+ return json.loads(self.cloud_manager.download_text_from_cloud(f'user_history/{conversation_id}/metadata'))
1294
+ except:
1295
+ return []
1296
+
1297
+ def download_conversation_message(msg_id):
1298
+ try:
1299
+ return self.cloud_manager.download_text_from_cloud(f'user_history/{conversation_id}/{msg_id}')
1300
+ except:
1301
+ return []
1302
+
1303
+ def golden_retriever(metadata_list):
1304
+ one_hour_ago = datetime.now() - timedelta(hours=1)
1305
+ return [metadata['M'] for metadata in reversed(metadata_list) if datetime.fromtimestamp(float(metadata['M'])) >= one_hour_ago]
1306
+
1307
+ def vector_search_conversation_history(message):
1308
+ return self.get_similar_local(message, vault=f'user_history/{conversation_id}/vectors')
1309
+
1310
+ history = ''
1311
+
1312
+ try:
1313
+ meta = download_conversation_metadata()
1314
+ except:
1315
+ meta = None
1316
+
1317
+ message_ids = golden_retriever(meta) if meta != None else []
1318
+
1319
+ update = T(target=self.update_conversation_history, args=(conversation_id, message, meta if meta else None))
1320
+ update.start()
1321
+
1322
+ print('message_ids:', message_ids) if self.verbose else None
1323
+
1324
+ if message_ids != []:
1325
+ history_lines = []
1326
+ with ThreadPoolExecutor(max_workers=10) as executor:
1327
+ future_to_message_id = {executor.submit(download_conversation_message, message_id): message_id for message_id in message_ids}
1328
+
1329
+ # Iterate over the futures as they complete (preserves order)
1330
+ for future in as_completed(future_to_message_id):
1331
+ message_content = future.result()
1332
+ history_lines.append(message_content)
1333
+
1334
+ history = '\n'.join(history_lines)
1335
+ history = "Recent conversation history:" + history
1336
+
1337
+ vector_similar_results = vector_search_conversation_history(message)
1338
+ vector_history = []
1339
+ now = datetime.now()
1340
+ for i in vector_similar_results:
1341
+ message_time = datetime.fromtimestamp(float(i['name']))
1342
+ data = i['data']
1343
+ vector_history.append(f'{get_time_statement(now, message_time)} {data}')
1344
+
1345
+ history = history + "Vector search conversation history:" + '\n'.join(vector_history)
1346
+
1347
+ update.join()
1348
+ return history
1349
+
1350
+
1253
1351
  class RateLimiter:
1254
1352
  def __init__(self, max_attempts=30):
1255
1353
  self.base_delay = 1 # Base delay of 1 second
@@ -1265,4 +1363,4 @@ class RateLimiter:
1265
1363
  def on_failure(self):
1266
1364
  # Apply exponential backoff with a random jitter
1267
1365
  self.current_delay = min(self.max_delay, random.uniform(self.base_delay, self.current_delay * self.backoff_factor))
1268
- time.sleep(self.current_delay)
1366
+ time.sleep(self.current_delay)
@@ -113,6 +113,34 @@ def call_update(email, vault, api):
113
113
  else:
114
114
  raise Exception(f"Request failed with status {response.status_code}")
115
115
 
116
+ def call_cloud_save(user, api_key, openai_key, vault, embeddings_model, text, meta = None, name = None, split = None, split_size = None):
117
+ url = "https://api.vectorvault.io/add_cloud"
118
+
119
+ # Define the data payload
120
+ data = {
121
+ 'user': user,
122
+ 'vault': vault,
123
+ 'api_key': api_key,
124
+ 'openai_key': openai_key,
125
+ "text": text,
126
+ "embeddings_model": embeddings_model,
127
+ "meta": meta,
128
+ "name": name,
129
+ "split": split,
130
+ "split_size": split_size,
131
+ }
132
+
133
+ # Make the POST request
134
+ response = requests.post(url, json=data)
135
+
136
+ # Check the request was successful
137
+ if response.status_code == 200:
138
+ # Parse the response JSON
139
+ data = response.json()
140
+ return data
141
+ else:
142
+ raise Exception(f"Request failed with status {response.status_code}")
143
+
116
144
 
117
145
  def call_get_chat(user, vault, api_key, openai_key, text, history=None, summary=False, get_context=False, n_context=4, return_context=False, expansion=False, history_search=False, model='gpt-3.5-turbo', include_context_meta=False):
118
146
  url = "https://api.vectorvault.io/get_chat"
File without changes
File without changes
File without changes