IO4IT 4.0.26__tar.gz → 4.0.27__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 (115) hide show
  1. {io4it-4.0.26 → io4it-4.0.27}/PKG-INFO +1 -1
  2. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/PKG-INFO +1 -1
  3. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/custom_api.py +7 -3
  4. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWCustomLLMApi.py +36 -4
  5. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWDoclingToMarkdown.py +51 -0
  6. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owcustomllmapi.ui +40 -11
  7. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owfile2images.ui +4 -4
  8. {io4it-4.0.26 → io4it-4.0.27}/setup.py +1 -1
  9. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/SOURCES.txt +0 -0
  10. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/dependency_links.txt +0 -0
  11. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/entry_points.txt +0 -0
  12. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/namespace_packages.txt +0 -0
  13. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/requires.txt +0 -0
  14. {io4it-4.0.26 → io4it-4.0.27}/io4it.egg-info/top_level.txt +0 -0
  15. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/__init__.py +0 -0
  16. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/ocr_function/__init__.py +0 -0
  17. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/ocr_function/word_converter.py +0 -0
  18. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/toolViews/designer/key_manager_ui.ui +0 -0
  19. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/toolViews/designer/link_workflow_manager_ui.ui +0 -0
  20. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/toolViews/key_manager_ui.py +0 -0
  21. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/toolViews/linkHTLMWorkflow_manager_ui.py +0 -0
  22. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/__init__.py +0 -0
  23. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/environment_collector.py +0 -0
  24. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/file2images.py +0 -0
  25. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/keys_manager.py +0 -0
  26. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/mail.py +0 -0
  27. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pool_exec_utils.py +0 -0
  28. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pst_extractor/TigerODMPST.dll +0 -0
  29. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pst_extractor/__init__.py +0 -0
  30. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pst_extractor/export_pst.py +0 -0
  31. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pst_extractor/libpff.dll +0 -0
  32. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/pst_extractor/z.dll +0 -0
  33. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/secret_manager.py +0 -0
  34. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/utils/utils_md.py +0 -0
  35. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWChatGpt.py +0 -0
  36. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWDeep_Search.py +0 -0
  37. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWExportMarkdown.py +0 -0
  38. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWExtractTablesDocxToXlsx.py +0 -0
  39. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWInboxMailMonitoring.py +0 -0
  40. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWJsonToDataTable.py +0 -0
  41. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWMD2HTML.py +0 -0
  42. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWMarkdownLoader.py +0 -0
  43. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWMarkdownizer.py +0 -0
  44. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWOfficeNormalizer.py +0 -0
  45. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWPPTX2JPG.py +0 -0
  46. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWParserHTML.py +0 -0
  47. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWPdfType.py +0 -0
  48. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWPipAuditor.py +0 -0
  49. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWProcessPoolExecutor.py +0 -0
  50. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWS3Uploader.py +0 -0
  51. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWS3downloader.py +0 -0
  52. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWS3list.py +0 -0
  53. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWWebSearch.py +0 -0
  54. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWXLSX_insert.py +0 -0
  55. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWmailLoader.py +0 -0
  56. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWmailSender.py +0 -0
  57. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/OWwordpdf2docx.py +0 -0
  58. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/__init__.py +0 -0
  59. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/__init__.py +0 -0
  60. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/nogui.ui +0 -0
  61. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/ow_extract_tables_docx_to_xlsx.ui +0 -0
  62. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/ow_file_ext_selector.ui +0 -0
  63. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/ow_json_to_data_table.ui +0 -0
  64. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owchatgpt.ui +0 -0
  65. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owdeepsearch.ui +0 -0
  66. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owdoclingtomarkdown.ui +0 -0
  67. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owexportmarkdown.ui +0 -0
  68. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owinboxmailmonitoring.ui +0 -0
  69. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owinsertdatatoexcel.ui +0 -0
  70. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owmailloader.ui +0 -0
  71. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owmailsender.ui +0 -0
  72. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owmarkdownizer.ui +0 -0
  73. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owmarkdownloader.ui +0 -0
  74. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owmd2html.ui +0 -0
  75. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owofficenormalizer.ui +0 -0
  76. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owparserhtml.ui +0 -0
  77. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owpdftype.ui +0 -0
  78. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owpipauditor.ui +0 -0
  79. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owprocesspoolexecutor.ui +0 -0
  80. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owspeechtotext.ui +0 -0
  81. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owvisualizationer.ui +0 -0
  82. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/owwebsearch.ui +0 -0
  83. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/designer/wordpdf2docx.ui +0 -0
  84. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/__init__.py +0 -0
  85. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/chatgpt.png +0 -0
  86. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/check_pdf.png +0 -0
  87. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/deepsearch.svg +0 -0
  88. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/dep_md_old.png +0 -0
  89. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/download.png +0 -0
  90. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/export_md.png +0 -0
  91. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/extract_table.png +0 -0
  92. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/file2images.png +0 -0
  93. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/file_extensor.png +0 -0
  94. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/html.png +0 -0
  95. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/json-file.png +0 -0
  96. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/list_aws.png +0 -0
  97. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/load_md.png +0 -0
  98. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/mail_loader.png +0 -0
  99. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/mail_writer.png +0 -0
  100. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/md.png +0 -0
  101. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/monitor-email.svg +0 -0
  102. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/office_normalizer.png +0 -0
  103. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/owcustomllmapi.svg +0 -0
  104. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/owmd2html.svg +0 -0
  105. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/pip_auditor.png +0 -0
  106. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/process_pool_executor.png +0 -0
  107. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/speech_to_text.png +0 -0
  108. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/upload.png +0 -0
  109. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/visualizationer.png +0 -0
  110. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/websearch.png +0 -0
  111. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/wordpdf2docx.png +0 -0
  112. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons/xlsx_insert.png +0 -0
  113. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/IO4IT/widgets/icons_dev/__init__.py +0 -0
  114. {io4it-4.0.26 → io4it-4.0.27}/orangecontrib/__init__.py +0 -0
  115. {io4it-4.0.26 → io4it-4.0.27}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: io4it
3
- Version: 4.0.26
3
+ Version: 4.0.27
4
4
  Home-page:
5
5
  Author:
6
6
  Author-email:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: io4it
3
- Version: 4.0.26
3
+ Version: 4.0.27
4
4
  Home-page:
5
5
  Author:
6
6
  Author-email:
@@ -32,11 +32,14 @@ def search_for_streamed_token(decoded_line):
32
32
  dict_regex = r"\{[\s\S]*\}"
33
33
  match = re.search(dict_regex, decoded_line)
34
34
  if not match:
35
- return "Token not found"
36
-
35
+ return ""
36
+
37
37
  json_data = json.loads(match.group())
38
38
  delta = json_data["choices"][0]["delta"]
39
- token = next((delta[key] for key in known_keys if key in delta), "Token not found")
39
+ # .get(key) (not "key in delta") also skips a key whose value is
40
+ # JSON null, e.g. {"delta": {"role": "assistant", "content": null}},
41
+ # which some OpenAI-compatible servers (llama-server included) send.
42
+ token = next((delta[key] for key in known_keys if delta.get(key)), "")
40
43
  return token
41
44
 
42
45
 
@@ -58,6 +61,7 @@ def call_chat_completion(messages, config, stream=False, workflow_id="Request_RA
58
61
  "messages": messages,
59
62
  "temperature": config["temperature"],
60
63
  "max_tokens": config["max_tokens"],
64
+ "stream": stream,
61
65
  }
62
66
 
63
67
  kwargs = {"timeout": 600}
@@ -12,12 +12,14 @@ from AnyQt.QtWidgets import (
12
12
 
13
13
  if "site-packages/Orange/widgets" in os.path.dirname(os.path.abspath(__file__)).replace("\\", "/"):
14
14
  from Orange.widgets.orangecontrib.IO4IT.utils import custom_api
15
+ from Orange.widgets.orangecontrib.IO4IT.utils import keys_manager
15
16
  from Orange.widgets.orangecontrib.AAIT.utils import thread_management
16
17
  from Orange.widgets.orangecontrib.AAIT.llm.answers_llama import identify_table_type
17
18
  from Orange.widgets.orangecontrib.AAIT.utils.import_uic import uic
18
19
  from Orange.widgets.orangecontrib.AAIT.utils.initialize_from_ini import apply_modification_from_python_file
19
20
  else:
20
21
  from orangecontrib.IO4IT.utils import custom_api
22
+ from orangecontrib.IO4IT.utils import keys_manager
21
23
  from orangecontrib.AAIT.utils import thread_management
22
24
  from orangecontrib.AAIT.llm.answers_llama import identify_table_type
23
25
  from orangecontrib.AAIT.utils.import_uic import uic
@@ -51,6 +53,7 @@ class OWCustomLLMApi(widget.OWWidget):
51
53
  header_name = Setting("Authorization")
52
54
  auth_scheme = Setting("Bearer")
53
55
  api_key = Setting("")
56
+ key_service = Setting("")
54
57
  cert_path = Setting("")
55
58
  cert_is_ca = Setting(False)
56
59
  use_proxy = Setting(False)
@@ -79,7 +82,7 @@ class OWCustomLLMApi(widget.OWWidget):
79
82
  def __init__(self):
80
83
  super().__init__()
81
84
  self.setFixedWidth(700)
82
- self.setFixedHeight(695)
85
+ self.setFixedHeight(733)
83
86
  uic.loadUi(self.gui, self)
84
87
 
85
88
  self.label_description = self.findChild(QLabel, 'Description')
@@ -104,6 +107,10 @@ class OWCustomLLMApi(widget.OWWidget):
104
107
  self.line_api_key.setText(self.api_key)
105
108
  self.line_api_key.editingFinished.connect(self.update_parameters)
106
109
 
110
+ self.line_key_service = self.findChild(QLineEdit, 'lineKeyService')
111
+ self.line_key_service.setText(self.key_service)
112
+ self.line_key_service.editingFinished.connect(self.update_parameters)
113
+
107
114
  self.line_cert_path = self.findChild(QLineEdit, 'lineCertPath')
108
115
  self.line_cert_path.setText(self.cert_path)
109
116
  self.line_cert_path.editingFinished.connect(self.update_parameters)
@@ -144,6 +151,7 @@ class OWCustomLLMApi(widget.OWWidget):
144
151
  self.thread = None
145
152
  self.can_run = True
146
153
  self.result = None
154
+ self._resolved_api_key = None
147
155
 
148
156
  self.post_initialized()
149
157
 
@@ -153,6 +161,7 @@ class OWCustomLLMApi(widget.OWWidget):
153
161
  self.header_name = self.line_header_name.text().strip() or "Authorization"
154
162
  self.auth_scheme = self.line_auth_scheme.text().strip()
155
163
  self.api_key = self.line_api_key.text()
164
+ self.key_service = self.line_key_service.text().strip()
156
165
  self.cert_path = self.line_cert_path.text().strip()
157
166
  self.cert_is_ca = self.check_cert_is_ca.isChecked()
158
167
  self.use_proxy = self.check_use_proxy.isChecked()
@@ -167,13 +176,36 @@ class OWCustomLLMApi(widget.OWWidget):
167
176
  self.line_cert_path.setText(path)
168
177
  self.update_parameters()
169
178
 
179
+ def resolve_api_key(self):
180
+ """
181
+ Resolve the API key to actually use. A "Key manager service" name
182
+ takes priority: the real key is fetched from the secure key store
183
+ (Key Manager tool) and never persisted in the workflow file. Falls
184
+ back to the plain-text "API key" field (stored in the .ows file)
185
+ for legacy workflows that don't use the key store.
186
+ """
187
+ if self.key_service:
188
+ cfg = keys_manager.lire_config_api(self.key_service)
189
+ if not cfg or not cfg.get("api_key"):
190
+ self.error(
191
+ f"No API key found for service '{self.key_service}' in the Key Manager. "
192
+ f"Register it there (Create / overwrite api key), or clear this field to "
193
+ f"use the 'API key' field instead."
194
+ )
195
+ return None
196
+ return cfg["api_key"]
197
+ if not self.api_key:
198
+ self.error("API key is required (either via 'Key manager service' or the 'API key' field).")
199
+ return None
200
+ return self.api_key
201
+
170
202
  def build_config(self):
171
203
  return {
172
204
  "base_url": self.base_url,
173
205
  "route": self.route,
174
206
  "header_name": self.header_name,
175
207
  "auth_scheme": self.auth_scheme,
176
- "api_key": self.api_key,
208
+ "api_key": self._resolved_api_key,
177
209
  "cert_path": self.cert_path,
178
210
  "cert_is_ca": self.cert_is_ca,
179
211
  "use_proxy": self.use_proxy,
@@ -210,8 +242,8 @@ class OWCustomLLMApi(widget.OWWidget):
210
242
  self.Outputs.data.send(None)
211
243
  return
212
244
 
213
- if not self.api_key:
214
- self.error("API key is required.")
245
+ self._resolved_api_key = self.resolve_api_key()
246
+ if self._resolved_api_key is None:
215
247
  self.Outputs.data.send(None)
216
248
  return
217
249
 
@@ -14,6 +14,7 @@ from openpyxl import Workbook
14
14
  # --- Docling (unique lib utilisée pour la conversion) ---
15
15
  from docling.backend.pypdfium2_backend import PyPdfiumDocumentBackend
16
16
  from docling.datamodel.base_models import InputFormat
17
+ from docling.datamodel.pipeline_options import PdfPipelineOptions, RapidOcrOptions
17
18
  from docling.document_converter import (
18
19
  DocumentConverter,
19
20
  PdfFormatOption,
@@ -31,6 +32,45 @@ else:
31
32
  from orangecontrib.AAIT.utils.import_uic import uic
32
33
 
33
34
 
35
+ # --------- helpers Docling ----------
36
+ def _build_converter(force_ocr: bool = False):
37
+ """Construit le DocumentConverter.
38
+ force_ocr=False : configuration d'origine (options PDF par défaut).
39
+ force_ocr=True : OCR RapidOCR forcé sur toute la page (PDF image).
40
+ """
41
+ pdf_format = PdfFormatOption(
42
+ pipeline_cls=StandardPdfPipeline,
43
+ backend=PyPdfiumDocumentBackend
44
+ )
45
+ if force_ocr:
46
+ pdf_options = PdfPipelineOptions()
47
+ pdf_options.do_ocr = True
48
+ pdf_options.ocr_options = RapidOcrOptions(force_full_page_ocr=True)
49
+ pdf_format = PdfFormatOption(
50
+ pipeline_cls=StandardPdfPipeline,
51
+ backend=PyPdfiumDocumentBackend,
52
+ pipeline_options=pdf_options,
53
+ )
54
+
55
+ # Docling minimal config (inspiré du snippet)
56
+ return DocumentConverter(
57
+ allowed_formats=[InputFormat.PDF, InputFormat.DOCX, InputFormat.PPTX],
58
+ format_options={
59
+ InputFormat.PDF: pdf_format,
60
+ InputFormat.DOCX: WordFormatOption(
61
+ pipeline_cls=SimplePipeline
62
+ ),
63
+ # PPTX: pas d'option spécifique; géré par défaut
64
+ },
65
+ )
66
+
67
+
68
+ def _has_text(md: str, min_chars: int = 20) -> bool:
69
+ """True si le Markdown contient du vrai texte (hors commentaires <!-- image -->)."""
70
+ cleaned = re.sub(r"<!--.*?-->", "", md, flags=re.DOTALL)
71
+ return sum(c.isalnum() for c in cleaned) >= min_chars
72
+
73
+
34
74
  # --------- worker stateless : convertit 1 fichier avec Docling ----------
35
75
  def _convert_one_file(file_path_str: str):
36
76
  """Convertit un fichier (PDF/DOCX/PPTX) en Markdown via Docling.
@@ -67,7 +107,18 @@ def _convert_one_file(file_path_str: str):
67
107
  },
68
108
  )
69
109
  doc = doc_converter.convert(str(src)).document
110
+ md = doc.export_to_markdown(page_break_placeholder="<-!-- page-break --!->")
111
+ # 1er passage : comportement d'origine
112
+ doc = _build_converter().convert(str(src)).document
70
113
  md = doc.export_to_markdown()
114
+ message = ""
115
+
116
+ # Repli : PDF sans texte exploitable (PDF image) -> OCR pleine page
117
+ if src.suffix.lower() == ".pdf" and not _has_text(md):
118
+ doc = _build_converter(force_ocr=True).convert(str(src)).document
119
+ md = doc.export_to_markdown()
120
+ message = "ocr pleine page (pdf sans texte natif)"
121
+
71
122
  out_md.write_text(md, encoding="utf-8")
72
123
  status, message = "ok", ""
73
124
  except Exception as e:
@@ -7,7 +7,7 @@
7
7
  <x>0</x>
8
8
  <y>0</y>
9
9
  <width>700</width>
10
- <height>697</height>
10
+ <height>733</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="windowTitle">
@@ -35,7 +35,7 @@
35
35
  <x>20</x>
36
36
  <y>50</y>
37
37
  <width>661</width>
38
- <height>281</height>
38
+ <height>317</height>
39
39
  </rect>
40
40
  </property>
41
41
  <property name="title">
@@ -155,11 +155,14 @@
155
155
  <height>29</height>
156
156
  </rect>
157
157
  </property>
158
+ <property name="toolTip">
159
+ <string>Only used when "Key manager service" below is empty. Stored in plain text in the workflow file (.ows) — prefer the Key Manager service field when possible.</string>
160
+ </property>
158
161
  <property name="echoMode">
159
162
  <enum>QLineEdit::Password</enum>
160
163
  </property>
161
164
  </widget>
162
- <widget class="QLabel" name="labelCertPath">
165
+ <widget class="QLabel" name="labelKeyService">
163
166
  <property name="geometry">
164
167
  <rect>
165
168
  <x>10</x>
@@ -168,6 +171,32 @@
168
171
  <height>21</height>
169
172
  </rect>
170
173
  </property>
174
+ <property name="text">
175
+ <string>Key manager service :</string>
176
+ </property>
177
+ </widget>
178
+ <widget class="QLineEdit" name="lineKeyService">
179
+ <property name="geometry">
180
+ <rect>
181
+ <x>150</x>
182
+ <y>170</y>
183
+ <width>491</width>
184
+ <height>29</height>
185
+ </rect>
186
+ </property>
187
+ <property name="toolTip">
188
+ <string>Name of an API key registered via the standalone Key Manager tool ("Create / overwrite api key"). Takes priority over the API key field above and is never stored in the workflow file.</string>
189
+ </property>
190
+ </widget>
191
+ <widget class="QLabel" name="labelCertPath">
192
+ <property name="geometry">
193
+ <rect>
194
+ <x>10</x>
195
+ <y>210</y>
196
+ <width>131</width>
197
+ <height>21</height>
198
+ </rect>
199
+ </property>
171
200
  <property name="text">
172
201
  <string>.pem file :</string>
173
202
  </property>
@@ -176,7 +205,7 @@
176
205
  <property name="geometry">
177
206
  <rect>
178
207
  <x>150</x>
179
- <y>170</y>
208
+ <y>206</y>
180
209
  <width>410</width>
181
210
  <height>29</height>
182
211
  </rect>
@@ -186,7 +215,7 @@
186
215
  <property name="geometry">
187
216
  <rect>
188
217
  <x>566</x>
189
- <y>170</y>
218
+ <y>206</y>
190
219
  <width>75</width>
191
220
  <height>29</height>
192
221
  </rect>
@@ -199,7 +228,7 @@
199
228
  <property name="geometry">
200
229
  <rect>
201
230
  <x>150</x>
202
- <y>204</y>
231
+ <y>240</y>
203
232
  <width>491</width>
204
233
  <height>21</height>
205
234
  </rect>
@@ -212,7 +241,7 @@
212
241
  <property name="geometry">
213
242
  <rect>
214
243
  <x>10</x>
215
- <y>240</y>
244
+ <y>276</y>
216
245
  <width>131</width>
217
246
  <height>21</height>
218
247
  </rect>
@@ -228,7 +257,7 @@
228
257
  <property name="geometry">
229
258
  <rect>
230
259
  <x>150</x>
231
- <y>236</y>
260
+ <y>272</y>
232
261
  <width>491</width>
233
262
  <height>29</height>
234
263
  </rect>
@@ -242,7 +271,7 @@
242
271
  <property name="geometry">
243
272
  <rect>
244
273
  <x>20</x>
245
- <y>340</y>
274
+ <y>376</y>
246
275
  <width>661</width>
247
276
  <height>121</height>
248
277
  </rect>
@@ -339,7 +368,7 @@
339
368
  <property name="geometry">
340
369
  <rect>
341
370
  <x>20</x>
342
- <y>472</y>
371
+ <y>508</y>
343
372
  <width>661</width>
344
373
  <height>31</height>
345
374
  </rect>
@@ -352,7 +381,7 @@
352
381
  <property name="geometry">
353
382
  <rect>
354
383
  <x>20</x>
355
- <y>512</y>
384
+ <y>548</y>
356
385
  <width>661</width>
357
386
  <height>151</height>
358
387
  </rect>
@@ -7,19 +7,19 @@
7
7
  <x>0</x>
8
8
  <y>0</y>
9
9
  <width>470</width>
10
- <height>520</height>
10
+ <height>550</height>
11
11
  </rect>
12
12
  </property>
13
13
  <property name="minimumSize">
14
14
  <size>
15
15
  <width>470</width>
16
- <height>520</height>
16
+ <height>550</height>
17
17
  </size>
18
18
  </property>
19
19
  <property name="maximumSize">
20
20
  <size>
21
21
  <width>470</width>
22
- <height>520</height>
22
+ <height>550</height>
23
23
  </size>
24
24
  </property>
25
25
  <property name="windowTitle">
@@ -197,7 +197,7 @@ Input must contain a column with the absolute path to the .pptx / .pdf files.</s
197
197
  <x>160</x>
198
198
  <y>475</y>
199
199
  <width>281</width>
200
- <height>36</height>
200
+ <height>32</height>
201
201
  </rect>
202
202
  </property>
203
203
  <property name="styleSheet">
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  # Configuration
4
4
  NAME = "io4it"
5
- VERSION = "4.0.26"
5
+ VERSION = "4.0.27"
6
6
 
7
7
  INSTALL_REQUIRES = [
8
8
  "torchvision",
File without changes
File without changes