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.
Files changed (63) hide show
  1. {io4it-2.0.0 → io4it-2.1.0.2}/PKG-INFO +12 -2
  2. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/PKG-INFO +12 -2
  3. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/SOURCES.txt +2 -1
  4. io4it-2.1.0.2/io4it.egg-info/entry_points.txt +7 -0
  5. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/requires.txt +6 -1
  6. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/offuscation_basique.py +123 -16
  7. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWChatGpt.py +201 -0
  8. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +2 -1
  9. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +216 -0
  10. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +652 -0
  11. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +2 -1
  12. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3downloader.py +1 -0
  13. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWS3list.py +1 -0
  14. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWSpeechToText.py +1 -0
  15. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWmailLoader.py +3 -2
  16. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWmailSender.py +1 -0
  17. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +1 -0
  18. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +155 -0
  19. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +51 -0
  20. io4it-2.1.0.2/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +101 -0
  21. io4it-2.1.0.2/setup.py +74 -0
  22. io4it-2.0.0/io4it.egg-info/entry_points.txt +0 -2
  23. io4it-2.0.0/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -121
  24. io4it-2.0.0/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -153
  25. io4it-2.0.0/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -234
  26. io4it-2.0.0/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -59
  27. io4it-2.0.0/setup.py +0 -98
  28. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/dependency_links.txt +0 -0
  29. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/namespace_packages.txt +0 -0
  30. {io4it-2.0.0 → io4it-2.1.0.2}/io4it.egg-info/top_level.txt +0 -0
  31. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/__init__.py +0 -0
  32. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
  33. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
  34. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/__init__.py +0 -0
  35. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/utils/mail.py +0 -0
  36. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
  37. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
  38. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/chart.html +0 -0
  39. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
  40. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
  41. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
  42. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
  43. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
  44. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
  45. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
  46. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
  47. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
  48. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
  49. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
  50. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
  51. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
  52. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
  53. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
  54. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
  55. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
  56. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
  57. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
  58. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
  59. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
  60. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
  61. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
  62. {io4it-2.0.0 → io4it-2.1.0.2}/orangecontrib/__init__.py +0 -0
  63. {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.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.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
@@ -11,6 +11,11 @@ pyannote.audio
11
11
  pyannote-core
12
12
  pypandoc
13
13
  pypandoc-binary
14
- wave==0.0.2
15
14
  scikit-learn
16
15
  openai
16
+ pip-system-certs==5.0
17
+ docx2pdf
18
+ doc2docx
19
+ msal
20
+ exchangelib
21
+ CATEGORIT
@@ -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 = getpass.getpass("🔑 Clé API : ").strip()
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 = input("🔖 Nom du service : ").strip()
367
- cfg = lire_config_api(service)
368
- if cfg is None:
369
- print("erreur")
370
- return
371
- print(f"\n📄 service : {cfg['service']}")
372
- print(f"🔑 clé API : {cfg['api_key']}")
373
- if cfg['description']:
374
- print(f"📝 description : {cfg['description']}")
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
- choix = input("👉 Que faire ? [1-10] : ").strip()
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
- lecture_config_cli_oauth2()
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 LuuLM"
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"