sunholo 0.108.0__py3-none-any.whl → 0.109.1__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/genai/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
1
  from .process_funcs_cls import GenAIFunctionProcessor
2
2
  from .safety import genai_safety
3
- from .init import init_genai
3
+ from .init import init_genai
4
+ from .file_handling import download_gcs_upload_genai, construct_file_content
@@ -0,0 +1,186 @@
1
+ from ..custom_logging import log
2
+ from ..gcs import get_bytes_from_gcs
3
+
4
+ import mimetypes
5
+ import asyncio
6
+ import tempfile
7
+ import re
8
+ import traceback
9
+ try:
10
+ import google.generativeai as genai
11
+ from google.generativeai.types import file_types
12
+ except ImportError:
13
+ genai = None
14
+ file_types = None
15
+
16
+ DOCUMENT_MIMES = [
17
+ 'application/pdf',
18
+ 'application/x-javascript',
19
+ 'text/javascript',
20
+ 'application/x-python',
21
+ 'text/x-python',
22
+ 'text/plain',
23
+ 'text/html',
24
+ 'text/css',
25
+ 'text/md',
26
+ 'text/csv',
27
+ 'text/xml',
28
+ 'text/rtf'
29
+ ]
30
+
31
+ IMAGE_MIMES = [
32
+ 'image/png',
33
+ 'image/jpeg',
34
+ 'image/webp',
35
+ 'image/heic',
36
+ 'image/heif',
37
+ ]
38
+
39
+ VIDEO_MIMES = [
40
+ 'video/mp4',
41
+ 'video/mpeg',
42
+ 'video/mov',
43
+ 'video/avi',
44
+ 'video/x-flv',
45
+ 'video/mpg',
46
+ 'video/webm',
47
+ 'video/wmv',
48
+ 'video/3gpp'
49
+ ]
50
+
51
+ AUDIO_MIMES = [
52
+ 'audio/wav',
53
+ 'audio/mp3',
54
+ 'audio/aiff',
55
+ 'audio/aac',
56
+ 'audio/ogg',
57
+ 'audio/flac',
58
+ ]
59
+
60
+ ALLOWED_MIME_TYPES = set(AUDIO_MIMES + VIDEO_MIMES + IMAGE_MIMES + DOCUMENT_MIMES)
61
+
62
+ # 'documents':
63
+ # [
64
+ # {'storagePath': 'users/UQcKi4u7s...dsd.png',
65
+ # 'url': 'https://firebasestorage.googleapis.com/v0/b/multi...',
66
+ # 'contentType': 'image/png',
67
+ # 'type': 'image',
68
+ # 'name': 'multivac-data-architecture.png'},
69
+ # {'storagePath': 'users/UQc...3dc59e1.jpg',
70
+ # 'type': 'image',
71
+ # 'name': 'holosun-circle.jpg',
72
+ # 'url': 'https://firebasestorage.googleapis.com/v0/b/multiv...',
73
+ # 'contentType': 'image/jpeg'}
74
+ # ]
75
+ async def construct_file_content(gs_list, bucket:str):
76
+ """
77
+ Args:
78
+ - gs_list: a list of dicts representing files in a bucket
79
+ - contentType: The content type of the file on GCS
80
+ - storagePath: The path in the bucket
81
+ - bucket: The bucket the files are in
82
+
83
+ """
84
+
85
+ file_list = []
86
+ for element in gs_list:
87
+
88
+ the_mime_type = element.get('contentType')
89
+ if the_mime_type is None:
90
+ continue
91
+ if element.get('storagePath') is None:
92
+ continue
93
+ if the_mime_type in ALLOWED_MIME_TYPES:
94
+ file_list.append(element)
95
+
96
+ if not file_list:
97
+ return {"role": "user", "parts": [{"text": "No eligible contentTypes were found"}]}
98
+
99
+ content = []
100
+
101
+ # Loop through the valid files and process them
102
+ tasks = []
103
+ for file_info in file_list:
104
+ img_url = f"gs://{bucket}/{file_info['storagePath']}"
105
+ mime_type = file_info['contentType']
106
+ # Append the async download task to the task list
107
+ tasks.append(download_gcs_upload_genai(img_url, mime_type))
108
+
109
+ # Run all tasks in parallel
110
+ content = await asyncio.gather(*tasks)
111
+
112
+ return content
113
+
114
+ # Helper function to handle each file download with error handling
115
+ async def download_file_with_error_handling(img_url, mime_type):
116
+ try:
117
+ return await download_gcs_upload_genai(img_url, mime_type)
118
+ except Exception as err:
119
+ msg= f"Error processing file from {img_url}: {str(err)}"
120
+ log.error(msg)
121
+ return {"role": "user", "parts": [{"text": msg}]}
122
+
123
+ async def download_gcs_upload_genai(img_url, mime_type, retries=3, delay=2):
124
+ import aiofiles
125
+ """
126
+ Downloads and uploads a file with retries in case of failure.
127
+
128
+ Args:
129
+ - img_url: str The URL of the file to download.
130
+ - mime_type: str The MIME type of the file.
131
+ - retries: int Number of retry attempts before failing.
132
+ - delay: int Initial delay between retries, exponentially increasing.
133
+
134
+ Returns:
135
+ - downloaded_content: The result of the file upload if successful.
136
+ """
137
+ for attempt in range(retries):
138
+ try:
139
+ log.info(f"Upload {attempt} for {img_url=}")
140
+ # Download the file bytes asynchronously
141
+ file_bytes = await asyncio.to_thread(get_bytes_from_gcs, img_url)
142
+ if not file_bytes:
143
+ msg = f"Failed to download file from {img_url}: got None"
144
+ log.warning(msg)
145
+ return {"role": "user", "parts": [{"text": msg}]}
146
+
147
+ # Log the size of the file bytes
148
+ file_size = len(file_bytes)
149
+ log.info(f"Downloaded file size for {img_url}: {file_size} bytes")
150
+
151
+ if file_size > 19434343:
152
+ log.warning(f"File size for {img_url}: {file_size} is too big.")
153
+ msg = f"The file for {img_url} is too large ({file_size} bytes) to be used directly. Use RAG instead."
154
+ return {"role": "user", "parts": [{"text": msg}]}
155
+
156
+ extension = mimetypes.guess_extension(mime_type)
157
+
158
+ # Use aiofiles for asynchronous file operations
159
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=extension)
160
+ downloaded_file = temp_file.name
161
+
162
+ sanitized_file = re.sub(r'[^\w\-.]', '_', downloaded_file)
163
+
164
+ log.info(f"Writing file {sanitized_file}")
165
+ async with aiofiles.open(sanitized_file, 'wb') as f:
166
+ await f.write(file_bytes)
167
+
168
+ # Upload the file and get its content reference
169
+ try:
170
+ downloaded_content: file_types.File = await asyncio.to_thread(genai.upload_file, sanitized_file )
171
+ return {"role": "user", "parts": [{"file_data": downloaded_content}]}
172
+ except Exception as err:
173
+ msg = f"Could not upload {sanitized_file} to genai.upload_file: {str(err)} {traceback.format_exc()}"
174
+ log.error(msg)
175
+ return {"role": "user", "parts": [{"text": msg}]}
176
+
177
+ except Exception as err:
178
+ log.error(f"Error processing file {img_url} on attempt {attempt + 1}/{retries}: {str(err)}")
179
+
180
+ if attempt < retries - 1:
181
+ log.info(f"Retrying in {delay} seconds...")
182
+ await asyncio.sleep(delay)
183
+ delay *= 2 # Exponential backoff
184
+ else:
185
+ raise err # Raise the error after max retries
186
+
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.108.0
3
+ Version: 0.109.1
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.108.0.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.109.1.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -85,7 +85,8 @@ sunholo/gcs/download_folder.py,sha256=ijJTnS595JqZhBH8iHFErQilMbkuKgL-bnTCMLGuvl
85
85
  sunholo/gcs/download_url.py,sha256=Ul81n1rklr8WogPsuxWWD1Nr8RHU451LzHPMJNhAKzw,6416
86
86
  sunholo/gcs/extract_and_sign.py,sha256=paRrTCvCN5vkQwCB7OSkxWi-pfOgOtZ0bwdXE08c3Ps,1546
87
87
  sunholo/gcs/metadata.py,sha256=oQLcXi4brsZ74aegWyC1JZmhlaEV270HS5_UWtAYYWE,898
88
- sunholo/genai/__init__.py,sha256=dBl6IA3-Fx6-Vx81r0XqxHlUq6WeW1iDX188dpChu8s,115
88
+ sunholo/genai/__init__.py,sha256=6SWK7uV5F625J-P3xQoD6WKL59a9RSaidj-Guslyt8Q,192
89
+ sunholo/genai/file_handling.py,sha256=b3vT_MIsinJSMqEa1MoelmABPTZH5iXNdCLvA6z7Qg8,5995
89
90
  sunholo/genai/images.py,sha256=EyjsDqt6XQw99pZUQamomCpMOoIah9bp3XY94WPU7Ms,1678
90
91
  sunholo/genai/init.py,sha256=yG8E67TduFCTQPELo83OJuWfjwTnGZsyACospahyEaY,687
91
92
  sunholo/genai/process_funcs_cls.py,sha256=7_RQMqIAZ3nPP-GFgCHBvS39fwuWuGtvSyuJaJN_G3E,31590
@@ -149,9 +150,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
149
150
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
150
151
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
151
152
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
152
- sunholo-0.108.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
153
- sunholo-0.108.0.dist-info/METADATA,sha256=NoRyTCRajNC5x0uAVpyUs8Ogp9oCuChGi2rcMdpG5QQ,8670
154
- sunholo-0.108.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
155
- sunholo-0.108.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
156
- sunholo-0.108.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
157
- sunholo-0.108.0.dist-info/RECORD,,
153
+ sunholo-0.109.1.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
154
+ sunholo-0.109.1.dist-info/METADATA,sha256=U-SgUIRCDApHuOqKQPELVpyiTMqQo9TQ2E2HTNA0WuQ,8670
155
+ sunholo-0.109.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
156
+ sunholo-0.109.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
157
+ sunholo-0.109.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
158
+ sunholo-0.109.1.dist-info/RECORD,,