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.
- sunholo/components/retriever.py +7 -6
- sunholo/llamaindex/llamaindex_class.py +140 -2
- sunholo/llamaindex/user_history.py +61 -0
- sunholo/utils/config_class.py +1 -0
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/METADATA +2 -2
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/RECORD +10 -9
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/WHEEL +0 -0
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/entry_points.txt +0 -0
- {sunholo-0.79.5.dist-info → sunholo-0.80.0.dist-info}/top_level.txt +0 -0
sunholo/components/retriever.py
CHANGED
|
@@ -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(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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)
|
sunholo/utils/config_class.py
CHANGED
|
@@ -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.
|
|
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.
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
136
|
-
sunholo-0.
|
|
137
|
-
sunholo-0.
|
|
138
|
-
sunholo-0.
|
|
139
|
-
sunholo-0.
|
|
140
|
-
sunholo-0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|