kheopss commited on
Commit
7019ca9
1 Parent(s): 945d4e3

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +245 -0
  2. packages.txt +1 -0
  3. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os # Included to Python
2
+ from openai import OpenAI # OpenAI official Python package
3
+ from IPython.display import Audio # Included to Python
4
+
5
+ ## supporting functions
6
+ import base64, textwrap, time, openai, io
7
+ from PIL import Image # Pillow image library
8
+ import tempfile
9
+ from pdf2image import convert_from_path
10
+ import gradio as gr
11
+ from gradio_pdf import PDF
12
+
13
+ from dotenv import load_dotenv
14
+
15
+ load_dotenv()
16
+
17
+ openai_api_key = os.getenv('OPENAI_API_KE')
18
+ client = OpenAI(
19
+ api_key=openai_api_key)
20
+
21
+
22
+
23
+
24
+ def resize_image(image, max_dimension):
25
+ width, height = image.size
26
+
27
+ # Check if the image has a palette and convert it to true color mode
28
+ if image.mode == "P":
29
+ if "transparency" in image.info:
30
+ image = image.convert("RGBA")
31
+ else:
32
+ image = image.convert("RGB")
33
+
34
+ if width > max_dimension or height > max_dimension:
35
+ if width > height:
36
+ new_width = max_dimension
37
+ new_height = int(height * (max_dimension / width))
38
+ else:
39
+ new_height = max_dimension
40
+ new_width = int(width * (max_dimension / height))
41
+ image = image.resize((new_width, new_height), Image.LANCZOS)
42
+
43
+ timestamp = time.time()
44
+
45
+ return image
46
+
47
+ def convert_to_png(image):
48
+ with io.BytesIO() as output:
49
+ image.save(output, format="PNG")
50
+ return output.getvalue()
51
+
52
+ def process_image(path, max_size):
53
+ with Image.open(path) as image:
54
+ width, height = image.size
55
+ mimetype = image.get_format_mimetype()
56
+ if mimetype == "image/png" and width <= max_size and height <= max_size:
57
+ with open(path, "rb") as f:
58
+ encoded_image = base64.b64encode(f.read()).decode('utf-8')
59
+ return (encoded_image, max(width, height)) # returns a tuple consistently
60
+ else:
61
+ resized_image = resize_image(image, max_size)
62
+ png_image = convert_to_png(resized_image)
63
+ return (base64.b64encode(png_image).decode('utf-8'),
64
+ max(width, height) # same tuple metadata
65
+ )
66
+
67
+ def create_image_content(image, maxdim, detail_threshold):
68
+ detail = "low" if maxdim < detail_threshold else "high"
69
+ return {
70
+ "type": "image_url",
71
+ "image_url": {"url": f"data:image/jpeg;base64,{image}", "detail": detail}
72
+ }
73
+
74
+ def set_system_message(sysmsg):
75
+ return [{
76
+ "role": "system",
77
+ "content": sysmsg
78
+ }]
79
+
80
+ ## user message with images function
81
+ def set_user_message(user_msg_str,
82
+ file_path_list=[], # A list of file paths to images.
83
+ max_size_px=1024, # Shrink images for lower expense
84
+ file_names_list=None, # You can set original upload names to show AI
85
+ tiled=False, # True is the API Reference method
86
+ detail_threshold=700): # any images below this get 512px "low" mode
87
+
88
+ if not isinstance(file_path_list, list): # create empty list for weird input
89
+ file_path_list = []
90
+
91
+ if not file_path_list: # no files, no tiles
92
+ tiled = False
93
+
94
+ if file_names_list and len(file_names_list) == len(file_path_list):
95
+ file_names = file_names_list
96
+ else:
97
+ file_names = [os.path.basename(path) for path in file_path_list]
98
+
99
+ base64_images = [process_image(path, max_size_px) for path in file_path_list]
100
+
101
+ uploaded_images_text = ""
102
+ if file_names:
103
+ uploaded_images_text = "\n\n---\n\nUploaded images:\n" + '\n'.join(file_names)
104
+
105
+ if tiled:
106
+ content = [{"type": "text", "text": user_msg_str + uploaded_images_text}]
107
+ content += [create_image_content(image, maxdim, detail_threshold)
108
+ for image, maxdim in base64_images]
109
+ return [{"role": "user", "content": content}]
110
+ else:
111
+ return [{
112
+ "role": "user",
113
+ "content": ([user_msg_str + uploaded_images_text]
114
+ + [{"image": image} for image, _ in base64_images])
115
+ }]
116
+
117
+ poppler_path = '/usr/bin' # Adjust this path if needed
118
+
119
+ # Add the Poppler path to the system PATH
120
+ os.environ['PATH'] += os.pathsep + poppler_path
121
+
122
+ def pdf_to_images(pdf_path, dpi=300, output_format='JPEG'):
123
+ temp_dir = tempfile.mkdtemp()
124
+ pages = convert_from_path(pdf_path, dpi)
125
+ image_paths = []
126
+ for i, page in enumerate(pages):
127
+ image_path = os.path.join(temp_dir, f'page{i}.{output_format.lower()}')
128
+ page.save(image_path, output_format)
129
+ image_paths.append(image_path)
130
+ return image_paths
131
+
132
+ # -- START -- set up run variables
133
+
134
+ system_msg = """
135
+ You are kheops an AI assistant,you an accountant expert powered by kheops Team with computer vision.
136
+ AI knowledge cutoff: April 2024
137
+
138
+ Built-in vision capabilities:
139
+ - extract text from image
140
+ - describe images
141
+ - analyze image contents
142
+ - logical problem-solving requiring machine vision
143
+ """.strip()
144
+
145
+
146
+ """
147
+ Sachant que Total à payer doit etre egal à Fond travaux alur + Part charges prévisionnelles+ Part autres travaux - le solde précédent"""
148
+ # The user message
149
+ user_msg = """
150
+ Sachant que Total à payer = Fond travaux alur + Part charges prévisionnelles+ Part autres travaux - le solde précédent
151
+ fournit les informations suivante sous format json uniquement:
152
+ -Total à payer
153
+ -Fond travaux alur
154
+ -Total Part charges prévisionnelles
155
+ -Part autres travaux
156
+ -le solde précédent
157
+ -identifier le propriétaire
158
+ - l’adresse du propriétaire ou le numéro du lot du propriétaire si l'adresse n'est pas trouvé
159
+ - date du document
160
+ - date limit du payement
161
+ """.strip()
162
+
163
+
164
+
165
+
166
+
167
+ # user images file list, and max dimension limit
168
+ max_size = 1024 # downsizes if any dimension above this
169
+ # Définir le chemin du dossier contenant les images
170
+ def process(pdf):
171
+ """
172
+ if pdf == "PDF 1" :
173
+ PDF_PATH="_1.pdf"
174
+ elif pdf =="PDF 2" :
175
+ PDF_PATH="2.pdf"
176
+ elif pdf =="PDF 3" :
177
+ PDF_PATH="3.pdf"
178
+ elif pdf =="PDF 4" :
179
+ PDF_PATH="4.pdf"
180
+ elif pdf =="PDF 5" :
181
+ PDF_PATH="5.pdf"
182
+ elif pdf =="PDF 6" :
183
+ PDF_PATH="6.pdf"
184
+ """
185
+ image_paths = pdf_to_images(pdf)
186
+ system = set_system_message(system_msg)
187
+ chat_hist = [] # list of more user/assistant items
188
+ user = set_user_message(user_msg, image_paths, max_size)
189
+
190
+ params = { # dictionary format for ** unpacking
191
+ "model": "gpt-4o",
192
+ "temperature": 0.5,
193
+ "user": "my_customer",
194
+ "max_tokens": 500,
195
+ "top_p": 0.5,
196
+ "stream": True,
197
+ "messages": system + chat_hist + user,
198
+ }
199
+
200
+ start = time.perf_counter()
201
+ try:
202
+ client = openai.Client(timeout=111,api_key=openai_api_key)
203
+ response = client.chat.completions.with_raw_response.create(**params)
204
+ headers_dict = response.headers.items().mapping.copy()
205
+ for key, value in headers_dict.items(): # set a variable for each header
206
+ locals()[f'headers_{key.replace("-", "_")}'] = value
207
+ except Exception as e:
208
+ print(f"Error during API call: {e}")
209
+ return None
210
+ reply = ""
211
+ if response is not None:
212
+ try:
213
+
214
+ for chunk_no, chunk in enumerate(response.parse()):
215
+ # Ensure that delta.content is available
216
+ if hasattr(chunk.choices[0].delta, 'content'):
217
+ content = chunk.choices[0].delta.content
218
+ if content is None : content=""
219
+ reply += content
220
+ #print(content, end="") # Correct usage of end=""
221
+ # Only assign content to resultat
222
+ resultat = reply
223
+ else:
224
+ print("No content in chunk.")
225
+ except Exception as e:
226
+ print(f"Error during receive/parsing: {e}")
227
+
228
+ print(f"\n[elapsed: {time.perf_counter()-start:.2f} seconds]")
229
+
230
+
231
+ return resultat
232
+
233
+
234
+ iface = gr.Interface(
235
+ fn=process,
236
+ #inputs=gr.Radio(["PDF 1", "PDF 2", "PDF 3", "PDF 4", "PDF 5","PDF 6"]),
237
+ inputs= PDF(label="Upload a PDF", interactive=True),
238
+ outputs=[
239
+ #gr.File(label="Uploaded PDF"), # Display the uploaded PDF
240
+ gr.Textbox(label="Extracted Information") # Display processed result
241
+ ],
242
+ title="Immoblier",
243
+ description="Upload a PDF and extract the required information."
244
+ )
245
+ iface.launch(debug=True)
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ poppler-utils
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ pillow
4
+ pdf2image
5
+ ipython
6
+ python-dotenv
7
+ gradio-pdf