sunholo 0.76.6__py3-none-any.whl → 0.76.8__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/auth/run.py +1 -1
- sunholo/azure/event_grid.py +6 -4
- sunholo/vertex/__init__.py +2 -0
- sunholo/vertex/extensions_call.py +284 -0
- sunholo/vertex/extensions_class.py +17 -14
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/METADATA +2 -2
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/RECORD +11 -10
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/WHEEL +0 -0
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/entry_points.txt +0 -0
- {sunholo-0.76.6.dist-info → sunholo-0.76.8.dist-info}/top_level.txt +0 -0
sunholo/auth/run.py
CHANGED
|
@@ -66,7 +66,7 @@ def get_cloud_run_token(vector_name):
|
|
|
66
66
|
}
|
|
67
67
|
log.info(f"Authenticating for run_url {run_url} from {caller_frame.f_code.co_name}")
|
|
68
68
|
id_token = get_id_token(run_url)
|
|
69
|
-
log.info(f"id_token {id_token}")
|
|
69
|
+
#log.info(f"id_token {id_token}")
|
|
70
70
|
return id_token
|
|
71
71
|
|
|
72
72
|
def get_header(vector_name) -> Optional[dict]:
|
sunholo/azure/event_grid.py
CHANGED
|
@@ -12,9 +12,10 @@ def process_azure_blob_event(events: list) -> tuple:
|
|
|
12
12
|
tuple: A tuple containing the blob URL, attributes as metadata, and the vector name.
|
|
13
13
|
|
|
14
14
|
Example of Event Grid schema:
|
|
15
|
+
```
|
|
15
16
|
{
|
|
16
|
-
"topic": "/subscriptions/
|
|
17
|
-
"subject": "/blobServices/default/containers/
|
|
17
|
+
"topic": "/subscriptions/subscription-id/resourceGroups/resource-group/providers/Microsoft.Storage/storageAccounts/storage-account",
|
|
18
|
+
"subject": "/blobServices/default/containers/container/blobs/blob",
|
|
18
19
|
"eventType": "Microsoft.Storage.BlobCreated",
|
|
19
20
|
"eventTime": "2021-01-01T12:34:56.789Z",
|
|
20
21
|
"id": "event-id",
|
|
@@ -26,8 +27,8 @@ def process_azure_blob_event(events: list) -> tuple:
|
|
|
26
27
|
"contentType": "application/octet-stream",
|
|
27
28
|
"contentLength": 524288,
|
|
28
29
|
"blobType": "BlockBlob",
|
|
29
|
-
"url": "https://
|
|
30
|
-
"sequencer": "
|
|
30
|
+
"url": "https://storage-account.blob.core.windows.net/container/blob",
|
|
31
|
+
"sequencer": "0000000000000000000000000",
|
|
31
32
|
"storageDiagnostics": {
|
|
32
33
|
"batchId": "batch-id"
|
|
33
34
|
}
|
|
@@ -35,6 +36,7 @@ def process_azure_blob_event(events: list) -> tuple:
|
|
|
35
36
|
"dataVersion": "",
|
|
36
37
|
"metadataVersion": "1"
|
|
37
38
|
}
|
|
39
|
+
```
|
|
38
40
|
"""
|
|
39
41
|
storage_blob_created_event = "Microsoft.Storage.BlobCreated"
|
|
40
42
|
|
sunholo/vertex/__init__.py
CHANGED
|
@@ -2,3 +2,5 @@ from .init import init_vertex, init_genai
|
|
|
2
2
|
from .memory_tools import get_vertex_memories, print_grounding_response, get_google_search_grounding
|
|
3
3
|
from .safety import vertex_safety, genai_safety
|
|
4
4
|
from .extensions_class import VertexAIExtensions
|
|
5
|
+
from .extensions_call import get_extension_content
|
|
6
|
+
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
from .extensions_class import VertexAIExtensions
|
|
2
|
+
from ..utils import ConfigManager
|
|
3
|
+
from ..logging import log
|
|
4
|
+
import collections.abc
|
|
5
|
+
|
|
6
|
+
def get_extension_content(question: str, config: ConfigManager, project_id:str=None, **kwargs):
|
|
7
|
+
"""
|
|
8
|
+
Fetches content from extensions based on the provided question and configuration.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
question (str): The question to be processed by the extensions.
|
|
12
|
+
config (ConfigManager): The configuration manager instance.
|
|
13
|
+
**kwargs: Additional parameters to be passed to the extension.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
list: A list of responses from the extensions.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
Assuming a YAML configuration file as follows:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
kind: vacConfig
|
|
23
|
+
vac:
|
|
24
|
+
my_vac:
|
|
25
|
+
extensions:
|
|
26
|
+
- extension_id: 8524997435263549440
|
|
27
|
+
operation_id: post_our_new_energy_invoke_one_generic
|
|
28
|
+
vac: our_generic
|
|
29
|
+
operation_params:
|
|
30
|
+
input:
|
|
31
|
+
question: ""
|
|
32
|
+
chat_history: []
|
|
33
|
+
source_filters: []
|
|
34
|
+
source_filters_and_or: false
|
|
35
|
+
search_kwargs:
|
|
36
|
+
k: 0
|
|
37
|
+
filter: ""
|
|
38
|
+
fetch_k: 0
|
|
39
|
+
private_docs: []
|
|
40
|
+
whole_document: false
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The function can be called as:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
config = ConfigManager()
|
|
47
|
+
question = "What is the capital of France?"
|
|
48
|
+
|
|
49
|
+
responses = get_extension_content(
|
|
50
|
+
question=question,
|
|
51
|
+
config=config,
|
|
52
|
+
input={
|
|
53
|
+
"chat_history": [{"role": "user", "content": "Hello"}],
|
|
54
|
+
"source_filters": ["PPA/"],
|
|
55
|
+
"search_kwargs": {"k": 50, "filter": "source ILIKE '%GermanPolicyforPPA/%'", "fetch_k": 100}
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
In this example, `operation_params` will be updated to:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
{
|
|
64
|
+
"input": {
|
|
65
|
+
"question": "What is the capital of France?",
|
|
66
|
+
"chat_history": [{"role": "user", "content": "Hello"}],
|
|
67
|
+
"source_filters": ["PPA/"],
|
|
68
|
+
"source_filters_and_or": false,
|
|
69
|
+
"search_kwargs": {
|
|
70
|
+
"k": 50,
|
|
71
|
+
"filter": "source ILIKE '%GermanPolicyforPPA/%'",
|
|
72
|
+
"fetch_k": 100
|
|
73
|
+
},
|
|
74
|
+
"private_docs": [],
|
|
75
|
+
"whole_document": false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
"""
|
|
80
|
+
extensions = config.vacConfig('extensions')
|
|
81
|
+
responses = []
|
|
82
|
+
for tool in extensions:
|
|
83
|
+
try:
|
|
84
|
+
ve = VertexAIExtensions(project_id)
|
|
85
|
+
|
|
86
|
+
# Merge operation_params from tool config and **kwargs
|
|
87
|
+
operation_params = tool.get('operation_params', {})
|
|
88
|
+
log.info(f'{operation_params=}')
|
|
89
|
+
operation_params_input = update_nested_params(operation_params, kwargs)
|
|
90
|
+
|
|
91
|
+
# Update the question in operation_params if it exists
|
|
92
|
+
operation_params_input = inject_question(question, operation_params)
|
|
93
|
+
|
|
94
|
+
response = ve.execute_extension(
|
|
95
|
+
operation_id=tool['operation_id'],
|
|
96
|
+
operation_params=operation_params_input,
|
|
97
|
+
extension_id=tool.get('extension_id'),
|
|
98
|
+
extension_display_name=tool.get('extension_display_name'),
|
|
99
|
+
vac=tool.get('vac')
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Dynamically get keys for answer and metadata from YAML configuration
|
|
103
|
+
output_config = operation_params.get('output', {})
|
|
104
|
+
answer_key = output_config.get('answer', 'answer')
|
|
105
|
+
metadata_key = output_config.get('metadata', 'metadata')
|
|
106
|
+
|
|
107
|
+
# Extract answer and metadata based on the specified keys
|
|
108
|
+
log.info(f'{answer_key} {metadata_key}')
|
|
109
|
+
|
|
110
|
+
answer = extract_nested_value(response, answer_key)
|
|
111
|
+
metadata = extract_nested_value(response, metadata_key)
|
|
112
|
+
|
|
113
|
+
log.info(f'{answer=} {metadata=}')
|
|
114
|
+
|
|
115
|
+
if answer and metadata:
|
|
116
|
+
responses.append(f"{answer}\nMetadata: {metadata}")
|
|
117
|
+
elif answer:
|
|
118
|
+
responses.append(answer)
|
|
119
|
+
|
|
120
|
+
except Exception as err:
|
|
121
|
+
log.error(f'Could not find vertex-extension response: {str(err)}')
|
|
122
|
+
answer = None
|
|
123
|
+
|
|
124
|
+
log.info(f'Vertex extension responses: {responses=}')
|
|
125
|
+
|
|
126
|
+
answers = "\n\n".join([resp for resp in responses if resp is not None])
|
|
127
|
+
|
|
128
|
+
return answers
|
|
129
|
+
|
|
130
|
+
def update_nested_params(original, updates):
|
|
131
|
+
"""
|
|
132
|
+
Recursively update nested dictionaries with new values.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
original (dict): The original dictionary to be updated.
|
|
136
|
+
updates (dict): The new values to be merged into the original dictionary.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
dict: The updated dictionary.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
```python
|
|
143
|
+
original = {
|
|
144
|
+
"param1": "value1",
|
|
145
|
+
"nested_param": {
|
|
146
|
+
"sub_param1": "sub_value1"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
updates = {
|
|
151
|
+
"param1": "new_value1",
|
|
152
|
+
"nested_param": {
|
|
153
|
+
"sub_param1": "new_sub_value1"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
updated_params = update_nested_params(original, updates)
|
|
158
|
+
|
|
159
|
+
# updated_params will be:
|
|
160
|
+
# {
|
|
161
|
+
# "param1": "new_value1",
|
|
162
|
+
# "nested_param": {
|
|
163
|
+
# "sub_param1": "new_sub_value1"
|
|
164
|
+
# }
|
|
165
|
+
# }
|
|
166
|
+
```
|
|
167
|
+
"""
|
|
168
|
+
for key, value in updates.items():
|
|
169
|
+
if isinstance(value, collections.abc.Mapping):
|
|
170
|
+
original[key] = update_nested_params(original.get(key, {}), value)
|
|
171
|
+
else:
|
|
172
|
+
original[key] = value
|
|
173
|
+
return original
|
|
174
|
+
|
|
175
|
+
def inject_question(question, params):
|
|
176
|
+
"""
|
|
177
|
+
Recursively injects the question into nested dictionaries where the key is 'question' and the value is empty.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
question (str): The question to be injected.
|
|
181
|
+
params (dict): The dictionary where the question should be injected.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
dict: The dictionary with the question injected.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
```python
|
|
188
|
+
params = {
|
|
189
|
+
"input": {
|
|
190
|
+
"question": "",
|
|
191
|
+
"chat_history": [],
|
|
192
|
+
"source_filters": [],
|
|
193
|
+
"search_kwargs": {
|
|
194
|
+
"k": 0,
|
|
195
|
+
"filter": "",
|
|
196
|
+
"fetch_k": 0
|
|
197
|
+
},
|
|
198
|
+
"private_docs": [],
|
|
199
|
+
"whole_document": false
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
question = "What is the capital of France?"
|
|
204
|
+
|
|
205
|
+
updated_params = inject_question(question, params)
|
|
206
|
+
|
|
207
|
+
# updated_params will be:
|
|
208
|
+
# {
|
|
209
|
+
# "input": {
|
|
210
|
+
# "question": "What is the capital of France?",
|
|
211
|
+
# "chat_history": [],
|
|
212
|
+
# "source_filters": [],
|
|
213
|
+
# "search_kwargs": {
|
|
214
|
+
# "k": 0,
|
|
215
|
+
# "filter": "",
|
|
216
|
+
# "fetch_k": 0
|
|
217
|
+
# },
|
|
218
|
+
# "private_docs": [],
|
|
219
|
+
# "whole_document": false
|
|
220
|
+
# }
|
|
221
|
+
# }
|
|
222
|
+
```
|
|
223
|
+
"""
|
|
224
|
+
if isinstance(params, collections.abc.Mapping):
|
|
225
|
+
for key, value in params.items():
|
|
226
|
+
if isinstance(value, collections.abc.Mapping):
|
|
227
|
+
params[key] = inject_question(question, value)
|
|
228
|
+
elif key == 'question' and not value:
|
|
229
|
+
params[key] = question
|
|
230
|
+
return params
|
|
231
|
+
|
|
232
|
+
def extract_nested_value(data, key):
|
|
233
|
+
"""
|
|
234
|
+
Recursively extract a value from nested dictionaries based on the specified key or a dot-separated key path.
|
|
235
|
+
If the key is not dot-separated, it will find the first occurrence of that key in the nested dictionaries.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
data (dict): The dictionary to extract the value from.
|
|
239
|
+
key (str): The key or dot-separated key path to extract the value.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Any: The extracted value, or None if the key or key path is not found.
|
|
243
|
+
|
|
244
|
+
Example:
|
|
245
|
+
```python
|
|
246
|
+
data = {
|
|
247
|
+
"output": {
|
|
248
|
+
"content": "Some content",
|
|
249
|
+
"metadata": {"key1": "value1"}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
value = extract_nested_value(data, "content")
|
|
254
|
+
# value will be "Some content"
|
|
255
|
+
|
|
256
|
+
value = extract_nested_value(data, "output.metadata")
|
|
257
|
+
# value will be {"key1": "value1"}
|
|
258
|
+
```
|
|
259
|
+
"""
|
|
260
|
+
def search_key(data, key):
|
|
261
|
+
if isinstance(data, dict):
|
|
262
|
+
if key in data:
|
|
263
|
+
return data[key]
|
|
264
|
+
for k, v in data.items():
|
|
265
|
+
if isinstance(v, dict):
|
|
266
|
+
result = search_key(v, key)
|
|
267
|
+
if result is not None:
|
|
268
|
+
return result
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
if '.' in key:
|
|
272
|
+
keys = key.split('.')
|
|
273
|
+
for k in keys:
|
|
274
|
+
if isinstance(data, dict) and k in data:
|
|
275
|
+
data = data[k]
|
|
276
|
+
else:
|
|
277
|
+
return None
|
|
278
|
+
return data
|
|
279
|
+
else:
|
|
280
|
+
return search_key(data, key)
|
|
281
|
+
|
|
282
|
+
if __name__ == "__main__":
|
|
283
|
+
config = ConfigManager("one_ai")
|
|
284
|
+
get_extension_content("What are PPAs in france like?", config=config)
|
|
@@ -7,6 +7,7 @@ from .init import init_vertex
|
|
|
7
7
|
from ..logging import log
|
|
8
8
|
from ..utils.gcp_project import get_gcp_project
|
|
9
9
|
from ..utils.parsers import validate_extension_id
|
|
10
|
+
from ..utils.gcp import is_running_on_cloudrun
|
|
10
11
|
from ..auth import get_local_gcloud_token, get_cloud_run_token
|
|
11
12
|
import base64
|
|
12
13
|
import json
|
|
@@ -271,27 +272,31 @@ class VertexAIExtensions:
|
|
|
271
272
|
operation_id: str,
|
|
272
273
|
operation_params: dict,
|
|
273
274
|
extension_id: str=None,
|
|
275
|
+
extension_display_name: str=None,
|
|
274
276
|
vac: str=None):
|
|
275
277
|
|
|
276
|
-
if
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
278
|
+
if extension_display_name:
|
|
279
|
+
extensions = self.list_extensions()
|
|
280
|
+
for extension in extensions:
|
|
281
|
+
if extension.get('display_name') == extension_display_name:
|
|
282
|
+
log.info(f"Found extension_id for '{extension_display_name}'")
|
|
283
|
+
extension_id = extension['resource_name']
|
|
284
|
+
break
|
|
285
|
+
|
|
286
|
+
if extension_id:
|
|
281
287
|
extension_id = str(extension_id)
|
|
282
288
|
if not extension_id.startswith("projects/"):
|
|
283
289
|
extension_name = f"projects/{self.project_id}/locations/{self.location}/extensions/{extension_id}"
|
|
284
290
|
else:
|
|
285
291
|
extension_name = extension_id
|
|
292
|
+
else:
|
|
293
|
+
extension_name = self.created_extension.resource_name
|
|
294
|
+
if not extension_name:
|
|
295
|
+
raise ValueError("Must specify extension_id or extension_name - both were None")
|
|
286
296
|
|
|
287
297
|
extension = extensions.Extension(extension_name)
|
|
288
298
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
# local testing auth
|
|
292
|
-
from ..utils.gcp import is_running_on_cloudrun
|
|
293
|
-
auth_config=None # on cloud run it sorts itself out via default creds(?)
|
|
294
|
-
|
|
299
|
+
auth_config=None
|
|
295
300
|
if not is_running_on_cloudrun():
|
|
296
301
|
|
|
297
302
|
log.warning("Using local authentication via gcloud")
|
|
@@ -308,10 +313,8 @@ class VertexAIExtensions:
|
|
|
308
313
|
}
|
|
309
314
|
else:
|
|
310
315
|
log.warning("No vac configuration and not running locally so no authentication being set for this extension API call")
|
|
311
|
-
|
|
312
|
-
if auth_config:
|
|
313
|
-
log.info(f"{auth_config=}")
|
|
314
316
|
|
|
317
|
+
log.info(f"Executing extension {extension_name=} with {operation_id=} and {operation_params=}")
|
|
315
318
|
response = extension.execute(
|
|
316
319
|
operation_id=operation_id,
|
|
317
320
|
operation_params=operation_params,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.76.
|
|
3
|
+
Version: 0.76.8
|
|
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.76.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.76.8.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -20,9 +20,9 @@ sunholo/archive/archive.py,sha256=C-UhG5x-XtZ8VheQp92IYJqgD0V3NFQjniqlit94t18,11
|
|
|
20
20
|
sunholo/auth/__init__.py,sha256=TeP-OY0XGxYV_8AQcVGoh35bvyWhNUcMRfhuD5l44Sk,91
|
|
21
21
|
sunholo/auth/gcloud.py,sha256=PdbwkuTdRi4RKBmgG9uwsReegqC4VG15_tw5uzmA7Fs,298
|
|
22
22
|
sunholo/auth/refresh.py,sha256=uOdT7oQRVl0YsUP__NXj6PdUdLyXFSv2ylwF283esuw,1831
|
|
23
|
-
sunholo/auth/run.py,sha256=
|
|
23
|
+
sunholo/auth/run.py,sha256=3f2VKjIPePWz7o5EPetXF7JBCr-FNk9lOPJ-2qNTk0I,2724
|
|
24
24
|
sunholo/azure/__init__.py,sha256=S1WQ5jndzNgzhSBh9UpX_yw7hRVm3hCzkAWNxUdK4dA,48
|
|
25
|
-
sunholo/azure/event_grid.py,sha256=
|
|
25
|
+
sunholo/azure/event_grid.py,sha256=uXunwdjVLxNRf38aTRPoC9HXxFEFlL8JH9dijaOlF8M,2567
|
|
26
26
|
sunholo/bots/__init__.py,sha256=EMFd7e2z68l6pzYOnkzHbLd2xJRvxTKFRNCTuhZ8hIw,130
|
|
27
27
|
sunholo/bots/discord.py,sha256=cCFae5K1BCa6JVkWGLh_iZ9qFO1JpXb6K4eJrlDfEro,2442
|
|
28
28
|
sunholo/bots/github_webhook.py,sha256=5pQPRLM_wxxcILVaIzUDV8Kt7Arcm2dL1r1kMMHA524,9629
|
|
@@ -119,14 +119,15 @@ sunholo/utils/parsers.py,sha256=akLSZLdvHf5T9OKVj5C3bo4g3Y8puATd8rxnbB4tWDs,5419
|
|
|
119
119
|
sunholo/utils/timedelta.py,sha256=BbLabEx7_rbErj_YbNM0MBcaFN76DC4PTe4zD2ucezg,493
|
|
120
120
|
sunholo/utils/user_ids.py,sha256=SQd5_H7FE7vcTZp9AQuQDWBXd4FEEd7TeVMQe1H4Ny8,292
|
|
121
121
|
sunholo/utils/version.py,sha256=P1QAJQdZfT2cMqdTSmXmcxrD2PssMPEGM-WI6083Fck,237
|
|
122
|
-
sunholo/vertex/__init__.py,sha256=
|
|
123
|
-
sunholo/vertex/
|
|
122
|
+
sunholo/vertex/__init__.py,sha256=7x3piob_wd_MaJkhtOZy8wjZvhpEwdYCUuIeaJrFfg4,292
|
|
123
|
+
sunholo/vertex/extensions_call.py,sha256=dGCz-2j7k5qYgVOCjB8OJn_wB-YE5bFmBray21FC1qI,9169
|
|
124
|
+
sunholo/vertex/extensions_class.py,sha256=UARBnhOmxuqRGX3ooWrJujMXc0iKtx1HkCTnK0VEJmY,19006
|
|
124
125
|
sunholo/vertex/init.py,sha256=aLdNjrX3bUPfnWRhKUg5KUxSu0Qnq2YvuFNsgml4QEY,2866
|
|
125
126
|
sunholo/vertex/memory_tools.py,sha256=pomHrDKqvY8MZxfUqoEwhdlpCvSGP6KmFJMVKOimXjs,6842
|
|
126
127
|
sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
|
|
127
|
-
sunholo-0.76.
|
|
128
|
-
sunholo-0.76.
|
|
129
|
-
sunholo-0.76.
|
|
130
|
-
sunholo-0.76.
|
|
131
|
-
sunholo-0.76.
|
|
132
|
-
sunholo-0.76.
|
|
128
|
+
sunholo-0.76.8.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
129
|
+
sunholo-0.76.8.dist-info/METADATA,sha256=G2X8whSqUk6SyVj4nI2pBaRDfCdLr6XH_2076G-WK3o,7136
|
|
130
|
+
sunholo-0.76.8.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
131
|
+
sunholo-0.76.8.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
132
|
+
sunholo-0.76.8.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
133
|
+
sunholo-0.76.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|