IO4IT 2.0.0__tar.gz → 2.1.0.2__tar.gz
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.
- {io4it-2.0.0 → io4it-2.1.0.2}/PKG-INFO +12 -2
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/PKG-INFO +12 -2
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/SOURCES.txt +2 -1
- io4it-2.1.0.2/io4it.egg-info/entry_points.txt +7 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/requires.txt +6 -1
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/offuscation_basique.py +123 -16
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWChatGpt.py +201 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +2 -1
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +216 -0
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +652 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +2 -1
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3downloader.py +1 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3list.py +1 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWSpeechToText.py +1 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWmailLoader.py +3 -2
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWmailSender.py +1 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +1 -0
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +155 -0
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +51 -0
- io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +101 -0
- io4it-2.1.0.2/setup.py +74 -0
- io4it-2.0.0/io4it.egg-info/entry_points.txt +0 -2
- io4it-2.0.0/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -121
- io4it-2.0.0/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -153
- io4it-2.0.0/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -234
- io4it-2.0.0/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -59
- io4it-2.0.0/setup.py +0 -98
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/dependency_links.txt +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/namespace_packages.txt +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/top_level.txt +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/mail.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/chart.html +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/__init__.py +0 -0
- {io4it-2.0.0 → io4it-2.1.0.2}/setup.cfg +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: io4it
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.1.0.2
|
|
4
|
+
Summary: UNKNOWN
|
|
4
5
|
Home-page:
|
|
5
6
|
Author:
|
|
6
7
|
Author-email:
|
|
8
|
+
License: UNKNOWN
|
|
7
9
|
Keywords: orange3 add-on
|
|
10
|
+
Platform: UNKNOWN
|
|
8
11
|
Requires-Dist: pylatexenc
|
|
9
12
|
Requires-Dist: docopt
|
|
10
13
|
Requires-Dist: boto3
|
|
@@ -18,6 +21,13 @@ Requires-Dist: pyannote.audio
|
|
|
18
21
|
Requires-Dist: pyannote-core
|
|
19
22
|
Requires-Dist: pypandoc
|
|
20
23
|
Requires-Dist: pypandoc-binary
|
|
21
|
-
Requires-Dist: wave==0.0.2
|
|
22
24
|
Requires-Dist: scikit-learn
|
|
23
25
|
Requires-Dist: openai
|
|
26
|
+
Requires-Dist: pip-system-certs==5.0
|
|
27
|
+
Requires-Dist: docx2pdf
|
|
28
|
+
Requires-Dist: doc2docx
|
|
29
|
+
Requires-Dist: msal
|
|
30
|
+
Requires-Dist: exchangelib
|
|
31
|
+
Requires-Dist: CATEGORIT
|
|
32
|
+
|
|
33
|
+
UNKNOWN
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: io4it
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.1.0.2
|
|
4
|
+
Summary: UNKNOWN
|
|
4
5
|
Home-page:
|
|
5
6
|
Author:
|
|
6
7
|
Author-email:
|
|
8
|
+
License: UNKNOWN
|
|
7
9
|
Keywords: orange3 add-on
|
|
10
|
+
Platform: UNKNOWN
|
|
8
11
|
Requires-Dist: pylatexenc
|
|
9
12
|
Requires-Dist: docopt
|
|
10
13
|
Requires-Dist: boto3
|
|
@@ -18,6 +21,13 @@ Requires-Dist: pyannote.audio
|
|
|
18
21
|
Requires-Dist: pyannote-core
|
|
19
22
|
Requires-Dist: pypandoc
|
|
20
23
|
Requires-Dist: pypandoc-binary
|
|
21
|
-
Requires-Dist: wave==0.0.2
|
|
22
24
|
Requires-Dist: scikit-learn
|
|
23
25
|
Requires-Dist: openai
|
|
26
|
+
Requires-Dist: pip-system-certs==5.0
|
|
27
|
+
Requires-Dist: docx2pdf
|
|
28
|
+
Requires-Dist: doc2docx
|
|
29
|
+
Requires-Dist: msal
|
|
30
|
+
Requires-Dist: exchangelib
|
|
31
|
+
Requires-Dist: CATEGORIT
|
|
32
|
+
|
|
33
|
+
UNKNOWN
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
setup.cfg
|
|
2
1
|
setup.py
|
|
3
2
|
io4it.egg-info/PKG-INFO
|
|
4
3
|
io4it.egg-info/SOURCES.txt
|
|
@@ -30,10 +29,12 @@ orangecontrib/IO4IT/widgets/designer/__init__.py
|
|
|
30
29
|
orangecontrib/IO4IT/widgets/designer/chart.html
|
|
31
30
|
orangecontrib/IO4IT/widgets/designer/nogui.ui
|
|
32
31
|
orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui
|
|
32
|
+
orangecontrib/IO4IT/widgets/designer/owchatgpt.ui
|
|
33
33
|
orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui
|
|
34
34
|
orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui
|
|
35
35
|
orangecontrib/IO4IT/widgets/designer/owmailloader.ui
|
|
36
36
|
orangecontrib/IO4IT/widgets/designer/owmailsender.ui
|
|
37
|
+
orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui
|
|
37
38
|
orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui
|
|
38
39
|
orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui
|
|
39
40
|
orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
[orange.widgets]
|
|
2
|
+
AAIT - ALGORITHM = orangecontrib.ALGORITHM.widgets
|
|
3
|
+
AAIT - API = orangecontrib.API.widgets
|
|
4
|
+
AAIT - LLM INTEGRATION = orangecontrib.LLM_INTEGRATION.widgets
|
|
5
|
+
AAIT - MODELS = orangecontrib.LLM_MODELS.widgets
|
|
6
|
+
AAIT - TOOLBOX = orangecontrib.TOOLBOX.widgets
|
|
7
|
+
IO4IT = orangecontrib.IO4IT.widgets
|
|
@@ -2,6 +2,9 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import hashlib
|
|
4
4
|
import getpass
|
|
5
|
+
from cryptography.fernet import Fernet
|
|
6
|
+
import base64
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
7
10
|
from Orange.widgets.orangecontrib.AAIT.utils import MetManagement
|
|
@@ -47,12 +50,26 @@ def get_keys_dir(type_key: str = "MICROSOFT_EXCHANGE_OAUTH2") -> str:
|
|
|
47
50
|
if os.path.basename(base) != "keys":
|
|
48
51
|
base = os.path.join(base, "keys")
|
|
49
52
|
dossier = os.path.normpath(os.path.join(base, type_key))
|
|
50
|
-
print("dossier", dossier)
|
|
51
53
|
os.makedirs(dossier, exist_ok=True)
|
|
52
54
|
return dossier
|
|
53
55
|
except Exception as e:
|
|
54
56
|
raise RuntimeError(f"Erreur création/récupération dossier : {e}")
|
|
55
57
|
|
|
58
|
+
def get_fernet_key() -> bytes:
|
|
59
|
+
"""
|
|
60
|
+
Dérive une clé Fernet (32 octets base64) à partir du nom d'utilisateur local.
|
|
61
|
+
"""
|
|
62
|
+
username = getpass.getuser().encode("utf-8")
|
|
63
|
+
digest = hashlib.sha256(username).digest()
|
|
64
|
+
return base64.urlsafe_b64encode(digest[:32]) # 32 bytes en base64
|
|
65
|
+
|
|
66
|
+
def encrypt_secure(data: str) -> str:
|
|
67
|
+
fernet = Fernet(get_fernet_key())
|
|
68
|
+
return fernet.encrypt(data.encode("utf-8")).decode("utf-8")
|
|
69
|
+
|
|
70
|
+
def decrypt_secure(data: str) -> str:
|
|
71
|
+
fernet = Fernet(get_fernet_key())
|
|
72
|
+
return fernet.decrypt(data.encode("utf-8")).decode("utf-8")
|
|
56
73
|
|
|
57
74
|
|
|
58
75
|
# GSTION IMAP
|
|
@@ -160,6 +177,83 @@ def enregistrer_config_owa(mail,alias,server,username,password,interval):
|
|
|
160
177
|
print(f"❌ Erreur lors de l'enregistrement : {e}")
|
|
161
178
|
return 1
|
|
162
179
|
|
|
180
|
+
def enregistrer_config_owa_secure(mail, alias, server, username, password, interval):
|
|
181
|
+
try:
|
|
182
|
+
dossier = get_keys_dir("MICROSOFT_EXCHANGE_OWA_SECURE")
|
|
183
|
+
os.makedirs(dossier, exist_ok=True)
|
|
184
|
+
|
|
185
|
+
mdp_chiffre = encrypt_secure(password)
|
|
186
|
+
|
|
187
|
+
nom_fichier = os.path.join(dossier, f"{alias.replace('@', '_at_')}.json")
|
|
188
|
+
|
|
189
|
+
contenu = {
|
|
190
|
+
"mail": mail,
|
|
191
|
+
"alias": alias,
|
|
192
|
+
"server": server,
|
|
193
|
+
"username": username,
|
|
194
|
+
"password_encrypted": mdp_chiffre,
|
|
195
|
+
"interval_second": interval
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
with open(nom_fichier, "w", encoding="utf-8") as f:
|
|
199
|
+
json.dump(contenu, f, indent=4)
|
|
200
|
+
|
|
201
|
+
print(f"✅ Fichier OWA sécurisé enregistré : {nom_fichier}")
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
except Exception as e:
|
|
205
|
+
print(f"❌ Erreur d'enregistrement sécurisé OWA : {e}")
|
|
206
|
+
return 1
|
|
207
|
+
|
|
208
|
+
def enregistrer_config_cli_owa_secure():
|
|
209
|
+
print("\n🔐 Écriture fichier OWA avec chiffrement sécurisé :")
|
|
210
|
+
mail = input("📧 Mail (nom@domain.com) : ").strip()
|
|
211
|
+
alias = input("📛 Alias (visible) : ").strip()
|
|
212
|
+
server = input("🌐 Server : ").strip()
|
|
213
|
+
username = input("👤 Username (domain\\user) : ").strip()
|
|
214
|
+
password = input("🔑 Password (non masqué) : ").strip()
|
|
215
|
+
interval = int(input("⏱️ Interval (secondes) : ").strip())
|
|
216
|
+
if alias == "''" or alias == "\"\"" or alias == "":
|
|
217
|
+
alias = mail
|
|
218
|
+
|
|
219
|
+
if 0 != enregistrer_config_owa_secure(mail, alias, server, username, password, interval):
|
|
220
|
+
print("❌ Erreur lors de enregistrer_config_owa_secure !")
|
|
221
|
+
|
|
222
|
+
def lecture_config_cli_owa_secure():
|
|
223
|
+
fichier = input("📄 Nom du fichier JSON (sans chemin) : ").strip()
|
|
224
|
+
cfg = lire_config_owa_secure(fichier)
|
|
225
|
+
|
|
226
|
+
if cfg is None:
|
|
227
|
+
print("❌ Erreur")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
print(f"\n📧 mail : {cfg[0]}")
|
|
231
|
+
print(f"📛 alias : {cfg[1]}")
|
|
232
|
+
print(f"🌐 server : {cfg[2]}")
|
|
233
|
+
print(f"👤 username : {cfg[3]}")
|
|
234
|
+
print(f"🔑 password : {cfg[4]}")
|
|
235
|
+
print(f"⏱️ intervalle : {cfg[5]}s")
|
|
236
|
+
|
|
237
|
+
def lire_config_owa_secure(chemin_fichier):
|
|
238
|
+
try:
|
|
239
|
+
chemin_fichier = os.path.join(get_keys_dir("MICROSOFT_EXCHANGE_OWA_SECURE"), chemin_fichier)
|
|
240
|
+
with open(chemin_fichier, "r", encoding="utf-8") as f:
|
|
241
|
+
contenu = json.load(f)
|
|
242
|
+
|
|
243
|
+
mdp_dechiffre = decrypt_secure(contenu["password_encrypted"])
|
|
244
|
+
|
|
245
|
+
return [
|
|
246
|
+
contenu["mail"],
|
|
247
|
+
contenu["alias"],
|
|
248
|
+
contenu["server"],
|
|
249
|
+
contenu["username"],
|
|
250
|
+
mdp_dechiffre,
|
|
251
|
+
int(contenu["interval_second"])
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
print(f"❌ Erreur de lecture sécurisée OWA : {e}")
|
|
256
|
+
return None
|
|
163
257
|
|
|
164
258
|
|
|
165
259
|
# Fonction pour lire le fichier de configuration et déchiffrer le mot de passe
|
|
@@ -357,21 +451,28 @@ def lire_config_api(service_name):
|
|
|
357
451
|
def enregistrer_config_cli_api():
|
|
358
452
|
print("\n📝 Écriture d’une clé API :")
|
|
359
453
|
service = input("🔖 Nom du service : ").strip()
|
|
360
|
-
api_key =
|
|
454
|
+
api_key = input("🔑 Clé API : ").strip()
|
|
361
455
|
desc = input("✏️ Description : ").strip()
|
|
362
456
|
if 0 != enregistrer_config_api(service, api_key, desc):
|
|
363
457
|
print("erreur!")
|
|
364
458
|
|
|
365
|
-
def lire_config_cli_api():
|
|
366
|
-
service
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
print(f"
|
|
459
|
+
def lire_config_cli_api(service=""):
|
|
460
|
+
if service == "":
|
|
461
|
+
service = input("🔖 Nom du service : ").strip()
|
|
462
|
+
try:
|
|
463
|
+
cfg = lire_config_api(service)
|
|
464
|
+
if cfg is None:
|
|
465
|
+
print("erreur")
|
|
466
|
+
return
|
|
467
|
+
print(f"\n📄 service : {cfg['service']}")
|
|
468
|
+
print(f"🔑 clé API : {cfg['api_key']}")
|
|
469
|
+
if cfg['description']:
|
|
470
|
+
print(f"📝 description : {cfg['description']}")
|
|
471
|
+
return cfg['api_key']
|
|
472
|
+
except Exception as e:
|
|
473
|
+
print(f"❌ Erreur lors de la lecture : {e}")
|
|
474
|
+
return None
|
|
475
|
+
|
|
375
476
|
|
|
376
477
|
|
|
377
478
|
# Gestion d’éléments de nxp (DOSSIER_NODE_ID, SERVEUR, USERNAME, PASSWORD) (HARD dossier aait_store/keys)
|
|
@@ -458,8 +559,6 @@ def lire_config_cli_nxp():
|
|
|
458
559
|
print(f"📝 description : {cfg['description']}")
|
|
459
560
|
|
|
460
561
|
|
|
461
|
-
|
|
462
|
-
|
|
463
562
|
if __name__ == "__main__":
|
|
464
563
|
print("1) ecrire fichier IMAP4_SSL")
|
|
465
564
|
print("2) dechiffer fichier IMAP4_SSL")
|
|
@@ -471,7 +570,10 @@ if __name__ == "__main__":
|
|
|
471
570
|
print("8) Déchiffrer fichier Microsoft Exchange (OWA)")
|
|
472
571
|
print("9) Écrire fichier Microsoft Exchange (OAuth2)")
|
|
473
572
|
print("10) Déchiffrer fichier Microsoft Exchange (OAuth2)")
|
|
474
|
-
|
|
573
|
+
print("11) Écrire fichier Microsoft Exchange (OWA) [SECURE]")
|
|
574
|
+
print("12) Déchiffrer fichier Microsoft Exchange (OWA) [SECURE]")
|
|
575
|
+
|
|
576
|
+
choix = input("👉 Que faire ? [1-12] : ").strip()
|
|
475
577
|
|
|
476
578
|
if choix == "1":
|
|
477
579
|
enregistrer_config_cli_imap4_ssl()
|
|
@@ -492,7 +594,12 @@ if __name__ == "__main__":
|
|
|
492
594
|
elif choix=="9":
|
|
493
595
|
enregistrer_config_cli_oauth2()
|
|
494
596
|
elif choix=="10":
|
|
495
|
-
|
|
597
|
+
lire_config_cli_oauth2()
|
|
598
|
+
elif choix == "11":
|
|
599
|
+
enregistrer_config_cli_owa_secure()
|
|
600
|
+
elif choix == "12":
|
|
601
|
+
lecture_config_cli_owa_secure()
|
|
602
|
+
|
|
496
603
|
|
|
497
604
|
else:
|
|
498
605
|
print("❌ Choix invalide. Réessayez.\n")
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import base64
|
|
4
|
+
import ast
|
|
5
|
+
import openai
|
|
6
|
+
import Orange
|
|
7
|
+
from Orange.data import StringVariable
|
|
8
|
+
from Orange.widgets.widget import OWWidget, Input, Output
|
|
9
|
+
from Orange.widgets.settings import Setting
|
|
10
|
+
from AnyQt.QtWidgets import QApplication, QLineEdit, QSpinBox, QDoubleSpinBox, QPushButton
|
|
11
|
+
|
|
12
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
13
|
+
from Orange.widgets.orangecontrib.AAIT.utils import thread_management
|
|
14
|
+
from Orange.widgets.orangecontrib.AAIT.utils.import_uic import uic
|
|
15
|
+
from Orange.widgets.orangecontrib.IO4IT.utils import offuscation_basique
|
|
16
|
+
else:
|
|
17
|
+
from orangecontrib.AAIT.utils.import_uic import uic
|
|
18
|
+
from orangecontrib.AAIT.utils import thread_management
|
|
19
|
+
from orangecontrib.IO4IT.utils import offuscation_basique
|
|
20
|
+
|
|
21
|
+
class ChatGpt(OWWidget):
|
|
22
|
+
name = "CallChatGptApi"
|
|
23
|
+
description = "Call to chatgpt API. You need to provide a prompt and an api_keys. You call also add an image_paths and a system_prompt if you want."
|
|
24
|
+
icon = "icons/chatgpt.png"
|
|
25
|
+
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
26
|
+
icon = "icons_dev/chatgpt.png"
|
|
27
|
+
priority = 3000
|
|
28
|
+
max_tokens = Setting(4096)
|
|
29
|
+
temperature = Setting(0)
|
|
30
|
+
model = Setting("gpt-4o")
|
|
31
|
+
gui = os.path.join(os.path.dirname(os.path.abspath(__file__)), "designer/owchatgpt.ui")
|
|
32
|
+
want_control_area = False
|
|
33
|
+
category = "AAIT - LLM INTEGRATION"
|
|
34
|
+
|
|
35
|
+
class Inputs:
|
|
36
|
+
data = Input("Data", Orange.data.Table)
|
|
37
|
+
|
|
38
|
+
@Inputs.data
|
|
39
|
+
def set_data(self, in_data):
|
|
40
|
+
if in_data is None:
|
|
41
|
+
return
|
|
42
|
+
if "prompt" not in in_data.domain:
|
|
43
|
+
self.error("input table need a prompt column")
|
|
44
|
+
return
|
|
45
|
+
self.prompt = in_data.get_column("prompt")[0]
|
|
46
|
+
if "image_paths" in in_data.domain:
|
|
47
|
+
self.image_paths = in_data.get_column("image_paths")[0]
|
|
48
|
+
if "api_keys" not in in_data.domain:
|
|
49
|
+
self.api_keys = offuscation_basique.lire_config_cli_api("CHAT_GPT")
|
|
50
|
+
if offuscation_basique.lire_config_cli_api("CHAT_GPT") is None:
|
|
51
|
+
self.error("input table need a api_keys column or a folder API with you CHAT_GPT keys in aait store")
|
|
52
|
+
if self.api_keys is None:
|
|
53
|
+
self.api_keys = in_data.get_column("api_keys")[0]
|
|
54
|
+
if "system_prompt" in in_data.domain:
|
|
55
|
+
self.system_prompt = in_data.get_column("system_prompt")[0]
|
|
56
|
+
self.data = in_data
|
|
57
|
+
self.run()
|
|
58
|
+
|
|
59
|
+
class Outputs:
|
|
60
|
+
data = Output("Data", Orange.data.Table)
|
|
61
|
+
|
|
62
|
+
def __init__(self):
|
|
63
|
+
super().__init__()
|
|
64
|
+
# Qt Management
|
|
65
|
+
self.setFixedWidth(400)
|
|
66
|
+
self.setFixedHeight(300)
|
|
67
|
+
uic.loadUi(self.gui, self)
|
|
68
|
+
self.edit_model = self.findChild(QLineEdit, 'modelName')
|
|
69
|
+
self.edit_model.setPlaceholderText("Model name")
|
|
70
|
+
self.edit_model.setText(self.model)
|
|
71
|
+
self.edit_model.editingFinished.connect(self.update_parameters)
|
|
72
|
+
|
|
73
|
+
self.edit_max_tokens = self.bind_spinbox("boxMaxTokens", self.max_tokens)
|
|
74
|
+
self.edit_temperature = self.bind_spinbox("boxTemperature", self.temperature, is_double=True)
|
|
75
|
+
self.pushButton_call_api =self.findChild(QPushButton, 'callApi')
|
|
76
|
+
self.pushButton_call_api.clicked.connect(self.run)
|
|
77
|
+
|
|
78
|
+
self.data = None
|
|
79
|
+
self.prompt = None
|
|
80
|
+
self.image_paths = None
|
|
81
|
+
self.api_keys = None
|
|
82
|
+
self.system_prompt = ""
|
|
83
|
+
self.text_response = None
|
|
84
|
+
self.thread = None
|
|
85
|
+
self.run()
|
|
86
|
+
|
|
87
|
+
def bind_spinbox(self, name, value, is_double=False):
|
|
88
|
+
widget_type = QDoubleSpinBox if is_double else QSpinBox
|
|
89
|
+
box = self.findChild(widget_type, name)
|
|
90
|
+
box.setValue(value)
|
|
91
|
+
box.editingFinished.connect(self.update_parameters)
|
|
92
|
+
return box
|
|
93
|
+
|
|
94
|
+
def update_parameters(self):
|
|
95
|
+
self.max_tokens = self.edit_max_tokens.value()
|
|
96
|
+
self.temperature = self.edit_temperature.value()
|
|
97
|
+
self.model = self.edit_model.text()
|
|
98
|
+
|
|
99
|
+
def generate_answers(self):
|
|
100
|
+
try:
|
|
101
|
+
openai.api_key = self.api_keys
|
|
102
|
+
response = openai.chat.completions.create(
|
|
103
|
+
model=self.model,
|
|
104
|
+
messages=[
|
|
105
|
+
{
|
|
106
|
+
"role": "system",
|
|
107
|
+
"content": self.system_prompt
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"role": "user",
|
|
111
|
+
"content": self.prompt
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
max_tokens=self.max_tokens,
|
|
115
|
+
temperature=self.temperature
|
|
116
|
+
)
|
|
117
|
+
self.text_response = response.choices[0].message.content
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(e)
|
|
120
|
+
self.error(f"Error: {e}")
|
|
121
|
+
return
|
|
122
|
+
if self.text_response is None:
|
|
123
|
+
self.error("No response from chatgpt.")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def run(self):
|
|
127
|
+
self.error("")
|
|
128
|
+
self.warning("")
|
|
129
|
+
if self.data is None:
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
if self.prompt == "" or self.prompt is None:
|
|
133
|
+
self.error("No prompt provided.")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
if self.api_keys is None:
|
|
137
|
+
self.error("No api keys provided.")
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
# si on relance la génération par le bouton le prompt est déjà rempli (déjà une liste)
|
|
141
|
+
# mais on peut changer le model, la temp ou le nombre max de token
|
|
142
|
+
if isinstance(self.prompt, list):
|
|
143
|
+
self.prompt = [{"type": "text", "text": self.prompt}]
|
|
144
|
+
if self.image_paths is not None and self.image_paths != []:
|
|
145
|
+
if type(self.image_paths) == str:
|
|
146
|
+
self.image_paths = ast.literal_eval(self.image_paths)
|
|
147
|
+
for img_path in self.image_paths:
|
|
148
|
+
filename = os.path.basename(img_path)
|
|
149
|
+
|
|
150
|
+
# Ajoute une mention de l’image avant
|
|
151
|
+
self.prompt.append({
|
|
152
|
+
"type": "text",
|
|
153
|
+
"text": f"Photo : {filename}"
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
with open(img_path, "rb") as f:
|
|
157
|
+
b64_img = base64.b64encode(f.read()).decode("utf-8")
|
|
158
|
+
self.prompt.append({
|
|
159
|
+
"type": "image_url",
|
|
160
|
+
"image_url": {
|
|
161
|
+
"url": f"data:image/jpeg;base64,{b64_img}"
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
self.progressBarInit()
|
|
165
|
+
self.thread = thread_management.Thread(self.generate_answers)
|
|
166
|
+
self.thread.progress.connect(self.handle_progress)
|
|
167
|
+
self.thread.result.connect(self.handle_result)
|
|
168
|
+
self.thread.finish.connect(self.handle_finish)
|
|
169
|
+
self.thread.start()
|
|
170
|
+
|
|
171
|
+
def handle_progress(self, progress) -> None:
|
|
172
|
+
value = progress[0]
|
|
173
|
+
text = progress[1]
|
|
174
|
+
if value is not None:
|
|
175
|
+
self.progressBarSet(value)
|
|
176
|
+
if text is None:
|
|
177
|
+
self.textBrowser.setText("")
|
|
178
|
+
else:
|
|
179
|
+
self.textBrowser.insertPlainText(text)
|
|
180
|
+
|
|
181
|
+
def handle_result(self, result):
|
|
182
|
+
new_name_column = StringVariable("answer")
|
|
183
|
+
table = self.data.add_column(new_name_column, [self.text_response])
|
|
184
|
+
self.Outputs.data.send(table)
|
|
185
|
+
|
|
186
|
+
def handle_finish(self):
|
|
187
|
+
print("Generation finished")
|
|
188
|
+
self.progressBarFinished()
|
|
189
|
+
|
|
190
|
+
def post_initialized(self):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
app = QApplication(sys.argv)
|
|
195
|
+
my_widget = ChatGpt()
|
|
196
|
+
my_widget.show()
|
|
197
|
+
|
|
198
|
+
if hasattr(app, "exec"):
|
|
199
|
+
sys.exit(app.exec())
|
|
200
|
+
else:
|
|
201
|
+
sys.exit(app.exec_())
|
|
@@ -26,7 +26,8 @@ else:
|
|
|
26
26
|
@apply_modification_from_python_file(filepath_original_widget=__file__)
|
|
27
27
|
class OWDeep_Search(widget.OWWidget):
|
|
28
28
|
name = "Deep Search"
|
|
29
|
-
description = "Generate a response to a column 'prompt' with a
|
|
29
|
+
description = "Generate a response to a column 'prompt' with a LLM"
|
|
30
|
+
category = "AAIT - LLM INTEGRATION"
|
|
30
31
|
icon = "icons/deepsearch.svg"
|
|
31
32
|
if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
|
|
32
33
|
icon = "icons_dev/owqueryllm.svg"
|