sunholo 0.79.5__py3-none-any.whl → 0.80.0__py3-none-any.whl

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.
@@ -34,7 +34,7 @@ def load_memories(vector_name:str=None, config:ConfigManager=None):
34
34
 
35
35
  memories = config.vacConfig("memory")
36
36
 
37
- log.info(f"Found memory settings for {vector_name}: {memories}")
37
+ log.info(f"Found memory settings for {config.vector_name}: {memories}")
38
38
  if not memories or len(memories) == 0:
39
39
  log.info(f"No memory settings found for {vector_name}")
40
40
  return None
@@ -66,12 +66,13 @@ def pick_retriever(vector_name:str=None, config:ConfigManager=None, embeddings=N
66
66
  embeddings = embeddings or get_embeddings(config=config)
67
67
  read_only = value.get('read_only')
68
68
  try:
69
- vectorstore = pick_vectorstore(vectorstore,
70
- vector_name=vector_name,
71
- embeddings=embeddings,
72
- read_only=read_only)
69
+ vectorstore = pick_vectorstore(
70
+ vectorstore,
71
+ config=config,
72
+ embeddings=embeddings,
73
+ read_only=read_only)
73
74
  except Exception as e:
74
- log.error(f"Failed to pick_vectorstore {vectorstore} for {vector_name} - {str(e)} - skipping")
75
+ log.error(f"Failed to pick_vectorstore {vectorstore} for {config.vector_name} - {str(e)} - skipping")
75
76
  continue
76
77
 
77
78
  k_override = value.get('k', 3)
@@ -3,6 +3,7 @@ import tempfile
3
3
 
4
4
  try:
5
5
  from vertexai.preview import rag
6
+ from google.cloud.aiplatform_v1beta1 import RetrieveContextsResponse
6
7
  except ImportError:
7
8
  rag = None
8
9
 
@@ -37,12 +38,15 @@ class LlamaIndexVertexCorpusManager:
37
38
  ImportError: If vertexai is not installed.
38
39
  """
39
40
  from ..vertex.init import init_vertex
41
+
40
42
  if rag is None:
41
43
  raise ImportError("You must install vertexai via `pip install sunholo[gcp]`")
42
44
 
43
45
  self.config = config
44
46
  self.project_id = project_id
45
47
  self.location = location
48
+ self.corpus_display_name = ""
49
+ self.corpus = ""
46
50
 
47
51
  if config:
48
52
  self.project_id = self.config.vacConfig('project_id') or project_id
@@ -69,10 +73,13 @@ class LlamaIndexVertexCorpusManager:
69
73
 
70
74
  try:
71
75
  # Upload the temporary file
76
+ log.info(f"Uploading text:{text[:50]}... to {corpus_display_name}")
72
77
  uploaded_file = self.upload_file(temp_filename, corpus_display_name, description or text[:50])
73
78
  finally:
74
79
  # Clean up the temporary file
75
80
  os.remove(temp_filename)
81
+
82
+ log.info(f"Successfully uploaded text:{text[:50]}... to {corpus_display_name}")
76
83
 
77
84
  return uploaded_file
78
85
 
@@ -85,7 +92,7 @@ class LlamaIndexVertexCorpusManager:
85
92
  display_name=filename,
86
93
  description=description or f"Upload for {filename}",
87
94
  )
88
- log.info("Uploaded file: {rag_file}")
95
+ log.info(f"Uploaded file: {rag_file}")
89
96
 
90
97
  return rag_file
91
98
 
@@ -104,6 +111,89 @@ class LlamaIndexVertexCorpusManager:
104
111
 
105
112
  return response
106
113
 
114
+ def list_files(self, corpus_display_name:str):
115
+ corpus = self.find_corpus_from_list(corpus_display_name)
116
+ files = rag.list_files(corpus_name=corpus.name)
117
+
118
+ log.info(f"--Files in {corpus.name}:\n{files}")
119
+
120
+ return files
121
+
122
+ def find_file_from_list(self, display_name: str, corpus_display_name:str):
123
+ """
124
+ Finds a file from the list of files by its display name.
125
+
126
+ Args:
127
+ display_name (str): The display name of the file.
128
+ corpus_display_name (str): The display name of the corpus to look within
129
+
130
+ Returns:
131
+ The found file object if it exists, otherwise None.
132
+ """
133
+ files = self.list_files(corpus_display_name)
134
+ for file in files:
135
+ if display_name == file.display_name:
136
+ log.info(f"Found existing file with display name: {display_name}")
137
+
138
+ return file
139
+
140
+ return None
141
+
142
+ def get_file(self, file_display_name:str=None, file_name:str=None, corpus_display_name:str=None):
143
+
144
+ if file_display_name:
145
+ rag_file = self.find_file_from_list(file_display_name, corpus_display_name)
146
+ log.info(f"Found {rag_file} via display name: {file_display_name}")
147
+
148
+ return rag_file
149
+
150
+ if not file_name:
151
+ raise ValueError("Need to supply one of file_display_name or file_name")
152
+
153
+ corpus = self.find_corpus_from_list(corpus_display_name)
154
+ if file_name.startswith("projects/"):
155
+ rag_file = rag.get_file(name=file_name)
156
+ else:
157
+ if not corpus_display_name:
158
+ raise ValueError("Must supply corpus_display_name if not a full file_name")
159
+ rag_file = rag.get_file(name=file_name, corpus_name=corpus.name)
160
+
161
+ log.info(f"Found {rag_file}")
162
+
163
+ return rag_file
164
+
165
+ def delete_file(self, file_name, corpus_display_name:str):
166
+
167
+ corpus = self.find_corpus_from_list(corpus_display_name)
168
+ if file_name.startswith("projects/"):
169
+ rag.delete_file(name=file_name)
170
+ else:
171
+ if not corpus_display_name:
172
+ raise ValueError("Must supply corpus_display_name if not a full file_name")
173
+ rag.delete_file(name=file_name, corpus_name=corpus.name)
174
+
175
+ log.info(f"File {file_name} deleted.")
176
+
177
+ return True
178
+
179
+ def query_corpus(self, query:str, corpus_disply_name:str):
180
+ corpus = self.find_corpus_from_list(corpus_disply_name)
181
+
182
+ response:RetrieveContextsResponse = rag.retrieval_query(
183
+ rag_resources=[
184
+ rag.RagResource(
185
+ rag_corpus=corpus.name,
186
+ # Supply IDs from `rag.list_files()`.
187
+ # rag_file_ids=["rag-file-1", "rag-file-2", ...],
188
+ )
189
+ ],
190
+ text=query,
191
+ similarity_top_k=10, # Optional
192
+ vector_distance_threshold=0.5, # Optional
193
+ )
194
+
195
+ return response
196
+
107
197
  def list_corpora(self):
108
198
  """
109
199
  List all VertexAI Corpus for the project/location
@@ -120,10 +210,13 @@ class LlamaIndexVertexCorpusManager:
120
210
  Returns:
121
211
  The found corpus object if it exists, otherwise None.
122
212
  """
213
+ if display_name == self.corpus_display_name:
214
+ return self.corpus
123
215
  corpora = self.list_corpora()
124
216
  for corp in corpora:
125
217
  if display_name == corp.display_name:
126
218
  log.info(f"Found existing corpus with display name: {display_name}")
219
+ self.corpus = corp
127
220
  return corp
128
221
  return None
129
222
 
@@ -211,6 +304,29 @@ def llamaindex_command(args):
211
304
  manager.upload_file(filename=args.filename, corpus_display_name=args.display_name, description=args.description)
212
305
  elif args.action == "upload_text":
213
306
  manager.upload_text(text=args.text, corpus_display_name=args.display_name, description=args.description)
307
+ elif args.action == "list_files":
308
+ files = manager.list_files(corpus_display_name=args.display_name)
309
+ if files:
310
+ console.print(files)
311
+ else:
312
+ console.print("No files found for {args.display_name}")
313
+ elif args.action == "get_file":
314
+ file = manager.get_file(file_display_name=args.file_name, corpus_display_name=args.display_name)
315
+ console.print(file)
316
+ return file
317
+ elif args.action == "delete_file":
318
+ deleted = manager.delete_file(args.file_name, corpus_display_name=args.display_name)
319
+ if deleted:
320
+ console.print(f"Deleted {args.file_name}")
321
+ else:
322
+ console.print(f"ERROR: Could not delete {args.file_name}")
323
+
324
+ elif args.action == "query":
325
+ answer = manager.query_corpus(args.query, corpus_disply_name=args.display_name)
326
+ if answer:
327
+ console.print(answer)
328
+ else:
329
+ console.print(f"No answer found for {args.query} in {args.display_name}")
214
330
  else:
215
331
  console.print(f"Unknown action: {args.action}")
216
332
 
@@ -270,5 +386,27 @@ def setup_llamaindex_subparser(subparsers):
270
386
  upload_text_parser.add_argument('text', help='The text content to upload')
271
387
  upload_text_parser.add_argument('--description', help='Description of the text upload', default=None)
272
388
 
389
+ # LlamaIndex list_files command
390
+ list_files_parser = llamaindex_subparsers.add_parser('list_files', help='List all files in a corpus')
391
+ list_files_parser.add_argument('display_name', help='The name of the corpus')
392
+ list_files_parser.add_argument('vac', nargs='?', default="global", help='The VAC config to set it up for')
393
+
394
+ # LlamaIndex get_file command
395
+ get_file_parser = llamaindex_subparsers.add_parser('get_file', help='Get a file from a corpus')
396
+ get_file_parser.add_argument('display_name', help='The name of the corpus')
397
+ get_file_parser.add_argument('file_name', help='The name of the file to get')
398
+ get_file_parser.add_argument('vac', nargs='?', default="global", help='The VAC config to set it up for')
399
+
400
+ # LlamaIndex delete_file command
401
+ delete_file_parser = llamaindex_subparsers.add_parser('delete_file', help='Delete a file from a corpus')
402
+ delete_file_parser.add_argument('display_name', help='The name of the corpus')
403
+ delete_file_parser.add_argument('file_name', help='The name of the file to delete')
404
+ delete_file_parser.add_argument('vac', nargs='?', default="global", help='The VAC config to set it up for')
405
+
406
+ # LlamaIndex query command
407
+ query_parser = llamaindex_subparsers.add_parser('query', help='Query a corpus')
408
+ query_parser.add_argument('display_name', help='The name of the corpus')
409
+ query_parser.add_argument('query', help='The query string')
410
+ query_parser.add_argument('vac', nargs='?', default="global", help='The VAC config to set it up for')
411
+
273
412
  llamaindex_parser.set_defaults(func=llamaindex_command)
274
-
@@ -0,0 +1,61 @@
1
+ from ..utils import ConfigManager
2
+ from ..custom_logging import log
3
+ from .llamaindex_class import LlamaIndexVertexCorpusManager
4
+
5
+ import datetime
6
+
7
+ def add_user_history_rag(
8
+ user_id:str,
9
+ config:ConfigManager,
10
+ question:str,
11
+ answer:str,
12
+ metadata:dict={},
13
+ user_history_template:str=None):
14
+ # add user history to its own RAG store
15
+
16
+ log.info(f"Adding user history to RAG store: {question} and {answer}")
17
+
18
+ manager = LlamaIndexVertexCorpusManager(config)
19
+
20
+ corpus = manager.create_corpus(user_id, description=f"Personal user history for {user_id}")
21
+
22
+ current_datetime = datetime.datetime.now()
23
+
24
+ # Convert to string with desired format
25
+ current_datetime_str = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
26
+
27
+ if user_history_template is None:
28
+ user_history_template="""Question from {user_id} at {the_date}: {the_question}\nAnswer: {the_answer}\nMetadata:{the_metadata}"""
29
+
30
+ log.info(f"Found corpus for {user_id}: {corpus}")
31
+ user_history = user_history_template.format(
32
+ user_id=user_id,
33
+ the_date=current_datetime_str,
34
+ the_question=question,
35
+ the_answer=answer,
36
+ the_metadata=metadata
37
+ )
38
+
39
+ try:
40
+ manager.upload_text(
41
+ text=user_history,
42
+ corpus_display_name=user_id,
43
+ description=f"{user_id} chat history for {current_datetime}"
44
+ )
45
+ except Exception as err:
46
+ log.error(f"Could not upload LlamaIndex QNA RAG history: {str(err)}")
47
+
48
+ return user_history
49
+
50
+ def get_user_history_chunks(user_id:str, config:ConfigManager, query):
51
+
52
+ manager = LlamaIndexVertexCorpusManager(config)
53
+
54
+ manager.create_corpus(user_id)
55
+
56
+ response = manager.query_corpus(query, user_id)
57
+ user_history_memory = []
58
+ for chunk in response.contexts.contexts:
59
+ user_history_memory.append(chunk.text)
60
+
61
+ return "\n".join(user_history_memory)
@@ -159,6 +159,7 @@ class ConfigManager:
159
159
  if isinstance(value, dict) and key in dict1 and isinstance(dict1[key], dict):
160
160
  dict1[key] = self._merge_dicts(dict1[key], value)
161
161
  else:
162
+ print(f"Merging '{key}' to new '{value}'")
162
163
  dict1[key] = value
163
164
  return dict1
164
165
 
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.79.5
3
+ Version: 0.80.0
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.79.5.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.80.0.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -55,7 +55,7 @@ sunholo/cli/swagger.py,sha256=absYKAU-7Yd2eiVNUY-g_WLl2zJfeRUNdWQ0oH8M_HM,1564
55
55
  sunholo/cli/vertex.py,sha256=8130YCarxHL1UC3aqblNmUwGZTXbkdL4Y_FOnZJsWiI,2056
56
56
  sunholo/components/__init__.py,sha256=IDoylb74zFKo6NIS3RQqUl0PDFBGVxM1dfUmO7OJ44U,176
57
57
  sunholo/components/llm.py,sha256=5wRVf7lIb7q1vRADNcdQp26L9l4vGHFIvjtUDurZN_s,11488
58
- sunholo/components/retriever.py,sha256=KK8Df3NH8jNpEymPBfR2913rrQZN-PRMUGedtYNIV6k,6905
58
+ sunholo/components/retriever.py,sha256=ECTXmKiFYT7B9oFaXSEbmQ9m2OZOOpqzcPccFi9KDpc,6861
59
59
  sunholo/components/vectorstore.py,sha256=xKk7micTRwZckaI7U6PxvFz_ZSjCH48xPTDYiDcv2tc,5913
60
60
  sunholo/database/__init__.py,sha256=Zz0Shcq-CtStf9rJGIYB_Ybzb8rY_Q9mfSj-nviM490,241
61
61
  sunholo/database/alloydb.py,sha256=c1PEmK9fJCxYaVmKv4emvOoXrajV7KqaVK5mqpeksvM,11527
@@ -90,7 +90,8 @@ sunholo/langfuse/prompts.py,sha256=27BsVfihM6-h1jscbkGSO4HsATl-d4ZN6tcNCVztWoY,1
90
90
  sunholo/llamaindex/__init__.py,sha256=DlY_cHWCsVEV1C5WBgDdHRgOMlJc8pDoCRukUJ8PT9w,88
91
91
  sunholo/llamaindex/get_files.py,sha256=6rhXCDqQ_lrIapISQ_OYQDjiSATXvS_9m3qq53-oIl0,781
92
92
  sunholo/llamaindex/import_files.py,sha256=Bnic5wz8c61af9Kwq8KSrNBbc4imYnzMtBCb2jzSImI,6224
93
- sunholo/llamaindex/llamaindex_class.py,sha256=45N_xU0gDdgUPNv5m36DopRmG9JHFErwmgssKZ3wOqA,11058
93
+ sunholo/llamaindex/llamaindex_class.py,sha256=MYCgmJ2ebN0yrJbkshGIoaoX0pKi5yONLKbEPULDXRY,17049
94
+ sunholo/llamaindex/user_history.py,sha256=SwBNDOU2hsAzOMVwDjXQsR3d8RPUZofKTlZV_pAraO8,1934
94
95
  sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
96
  sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
96
97
  sunholo/patches/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -116,7 +117,7 @@ sunholo/utils/__init__.py,sha256=Hv02T5L2zYWvCso5hzzwm8FQogwBq0OgtUbN_7Quzqc,89
116
117
  sunholo/utils/api_key.py,sha256=Ct4bIAQZxzPEw14hP586LpVxBAVi_W9Serpy0BK-7KI,244
117
118
  sunholo/utils/big_context.py,sha256=gJIP7_ZL-YSLhOMq8jmFTMqH1wq8eB1NK7oKPeZAq2s,5578
118
119
  sunholo/utils/config.py,sha256=aG29MXcL5qzQMtCMqcdy-2ysDCYf9Zn_ZLk5NNOQNSE,8982
119
- sunholo/utils/config_class.py,sha256=DEY2w7ks5fXmtJ4SwqELhUF4NJFVOuRwxeVFZJlYPf0,8963
120
+ sunholo/utils/config_class.py,sha256=k2wKuvEQlbqfUzujGcrDH_ZuPEhtYrlksJr-sBy-elg,9022
120
121
  sunholo/utils/config_schema.py,sha256=Wv-ncitzljOhgbDaq9qnFqH5LCuxNv59dTGDWgd1qdk,4189
121
122
  sunholo/utils/gcp.py,sha256=uueODEpA-P6O15-t0hmcGC9dONLO_hLfzSsSoQnkUss,4854
122
123
  sunholo/utils/gcp_project.py,sha256=0ozs6tzI4qEvEeXb8MxLnCdEVoWKxlM6OH05htj7_tc,1325
@@ -132,9 +133,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
132
133
  sunholo/vertex/memory_tools.py,sha256=pgSahVDh7GPEulu3nl-w0jb5lTClb4TCnVxPnMokNZY,7533
133
134
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
134
135
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
135
- sunholo-0.79.5.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
136
- sunholo-0.79.5.dist-info/METADATA,sha256=B4e56x_LDOAPMTDXey40Sa4P0P9j69ZqL2pZUVjPaj8,7348
137
- sunholo-0.79.5.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
138
- sunholo-0.79.5.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
139
- sunholo-0.79.5.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
140
- sunholo-0.79.5.dist-info/RECORD,,
136
+ sunholo-0.80.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
137
+ sunholo-0.80.0.dist-info/METADATA,sha256=dL9qKmYHLd8M7rmztP84WKfSQ_aoVfLmwA6pU0E3jM0,7348
138
+ sunholo-0.80.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
139
+ sunholo-0.80.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
140
+ sunholo-0.80.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
141
+ sunholo-0.80.0.dist-info/RECORD,,