sunholo 0.76.7__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.
@@ -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)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.76.7
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.7.tar.gz
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
@@ -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=XH7FUKxdIgN9H2iDcWxL3sRnVHC3297G24RqEn4Ob0Y,240
122
+ sunholo/vertex/__init__.py,sha256=7x3piob_wd_MaJkhtOZy8wjZvhpEwdYCUuIeaJrFfg4,292
123
+ sunholo/vertex/extensions_call.py,sha256=dGCz-2j7k5qYgVOCjB8OJn_wB-YE5bFmBray21FC1qI,9169
123
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.7.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
128
- sunholo-0.76.7.dist-info/METADATA,sha256=CpUNghG6Y3R7SvzGvB-Mr4HVcMxh_H4fI8phNUs5SSw,7136
129
- sunholo-0.76.7.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
130
- sunholo-0.76.7.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
131
- sunholo-0.76.7.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
132
- sunholo-0.76.7.dist-info/RECORD,,
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,,