symbolicai 1.0.0__py3-none-any.whl → 1.1.0__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.
Files changed (127) hide show
  1. symai/__init__.py +198 -134
  2. symai/backend/base.py +51 -51
  3. symai/backend/engines/drawing/engine_bfl.py +33 -33
  4. symai/backend/engines/drawing/engine_gpt_image.py +4 -10
  5. symai/backend/engines/embedding/engine_llama_cpp.py +50 -35
  6. symai/backend/engines/embedding/engine_openai.py +22 -16
  7. symai/backend/engines/execute/engine_python.py +16 -16
  8. symai/backend/engines/files/engine_io.py +51 -49
  9. symai/backend/engines/imagecaptioning/engine_blip2.py +27 -23
  10. symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +53 -46
  11. symai/backend/engines/index/engine_pinecone.py +116 -88
  12. symai/backend/engines/index/engine_qdrant.py +1011 -0
  13. symai/backend/engines/index/engine_vectordb.py +78 -52
  14. symai/backend/engines/lean/engine_lean4.py +65 -25
  15. symai/backend/engines/neurosymbolic/__init__.py +28 -28
  16. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +137 -135
  17. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +145 -152
  18. symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
  19. symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +75 -49
  20. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +199 -155
  21. symai/backend/engines/neurosymbolic/engine_groq.py +106 -72
  22. symai/backend/engines/neurosymbolic/engine_huggingface.py +100 -67
  23. symai/backend/engines/neurosymbolic/engine_llama_cpp.py +121 -93
  24. symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +213 -132
  25. symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +180 -137
  26. symai/backend/engines/ocr/engine_apilayer.py +18 -20
  27. symai/backend/engines/output/engine_stdout.py +9 -9
  28. symai/backend/engines/{webscraping → scrape}/engine_requests.py +25 -11
  29. symai/backend/engines/search/engine_openai.py +95 -83
  30. symai/backend/engines/search/engine_parallel.py +665 -0
  31. symai/backend/engines/search/engine_perplexity.py +40 -41
  32. symai/backend/engines/search/engine_serpapi.py +33 -28
  33. symai/backend/engines/speech_to_text/engine_local_whisper.py +37 -27
  34. symai/backend/engines/symbolic/engine_wolframalpha.py +14 -8
  35. symai/backend/engines/text_to_speech/engine_openai.py +15 -19
  36. symai/backend/engines/text_vision/engine_clip.py +34 -28
  37. symai/backend/engines/userinput/engine_console.py +3 -4
  38. symai/backend/mixin/anthropic.py +48 -40
  39. symai/backend/mixin/deepseek.py +4 -5
  40. symai/backend/mixin/google.py +5 -4
  41. symai/backend/mixin/groq.py +2 -4
  42. symai/backend/mixin/openai.py +132 -110
  43. symai/backend/settings.py +14 -14
  44. symai/chat.py +164 -94
  45. symai/collect/dynamic.py +13 -11
  46. symai/collect/pipeline.py +39 -31
  47. symai/collect/stats.py +109 -69
  48. symai/components.py +556 -238
  49. symai/constraints.py +14 -5
  50. symai/core.py +1495 -1210
  51. symai/core_ext.py +55 -50
  52. symai/endpoints/api.py +113 -58
  53. symai/extended/api_builder.py +22 -17
  54. symai/extended/arxiv_pdf_parser.py +13 -5
  55. symai/extended/bibtex_parser.py +8 -4
  56. symai/extended/conversation.py +88 -69
  57. symai/extended/document.py +40 -27
  58. symai/extended/file_merger.py +45 -7
  59. symai/extended/graph.py +38 -24
  60. symai/extended/html_style_template.py +17 -11
  61. symai/extended/interfaces/blip_2.py +1 -1
  62. symai/extended/interfaces/clip.py +4 -2
  63. symai/extended/interfaces/console.py +5 -3
  64. symai/extended/interfaces/dall_e.py +3 -1
  65. symai/extended/interfaces/file.py +2 -0
  66. symai/extended/interfaces/flux.py +3 -1
  67. symai/extended/interfaces/gpt_image.py +15 -6
  68. symai/extended/interfaces/input.py +2 -1
  69. symai/extended/interfaces/llava.py +1 -1
  70. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
  71. symai/extended/interfaces/naive_vectordb.py +2 -2
  72. symai/extended/interfaces/ocr.py +4 -2
  73. symai/extended/interfaces/openai_search.py +2 -0
  74. symai/extended/interfaces/parallel.py +30 -0
  75. symai/extended/interfaces/perplexity.py +2 -0
  76. symai/extended/interfaces/pinecone.py +6 -4
  77. symai/extended/interfaces/python.py +2 -0
  78. symai/extended/interfaces/serpapi.py +2 -0
  79. symai/extended/interfaces/terminal.py +0 -1
  80. symai/extended/interfaces/tts.py +2 -1
  81. symai/extended/interfaces/whisper.py +2 -1
  82. symai/extended/interfaces/wolframalpha.py +1 -0
  83. symai/extended/metrics/__init__.py +1 -1
  84. symai/extended/metrics/similarity.py +5 -2
  85. symai/extended/os_command.py +31 -22
  86. symai/extended/packages/symdev.py +39 -34
  87. symai/extended/packages/sympkg.py +30 -27
  88. symai/extended/packages/symrun.py +46 -35
  89. symai/extended/repo_cloner.py +10 -9
  90. symai/extended/seo_query_optimizer.py +15 -12
  91. symai/extended/solver.py +104 -76
  92. symai/extended/summarizer.py +8 -7
  93. symai/extended/taypan_interpreter.py +10 -9
  94. symai/extended/vectordb.py +28 -15
  95. symai/formatter/formatter.py +39 -31
  96. symai/formatter/regex.py +46 -44
  97. symai/functional.py +184 -86
  98. symai/imports.py +85 -51
  99. symai/interfaces.py +1 -1
  100. symai/memory.py +33 -24
  101. symai/menu/screen.py +28 -19
  102. symai/misc/console.py +27 -27
  103. symai/misc/loader.py +4 -3
  104. symai/models/base.py +147 -76
  105. symai/models/errors.py +1 -1
  106. symai/ops/__init__.py +1 -1
  107. symai/ops/measures.py +17 -14
  108. symai/ops/primitives.py +933 -635
  109. symai/post_processors.py +28 -24
  110. symai/pre_processors.py +58 -52
  111. symai/processor.py +15 -9
  112. symai/prompts.py +714 -649
  113. symai/server/huggingface_server.py +115 -32
  114. symai/server/llama_cpp_server.py +14 -6
  115. symai/server/qdrant_server.py +206 -0
  116. symai/shell.py +98 -39
  117. symai/shellsv.py +307 -223
  118. symai/strategy.py +135 -81
  119. symai/symbol.py +276 -225
  120. symai/utils.py +62 -46
  121. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +19 -9
  122. symbolicai-1.1.0.dist-info/RECORD +168 -0
  123. symbolicai-1.0.0.dist-info/RECORD +0 -163
  124. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
  125. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
  126. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
  127. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/top_level.txt +0 -0
symai/functional.py CHANGED
@@ -49,13 +49,12 @@ class ProbabilisticBooleanMode(Enum):
49
49
  ENGINE_PROBABILISTIC_BOOLEAN_MODE = ProbabilisticBooleanMode.MEDIUM
50
50
 
51
51
 
52
-
53
52
  def _probabilistic_bool(rsp: str, mode=ProbabilisticBooleanMode.TOLERANT) -> bool:
54
53
  if rsp is None:
55
54
  return False
56
55
  # check if rsp is a string / hard match
57
56
  val = str(rsp).lower()
58
- if mode == ProbabilisticBooleanMode.STRICT:
57
+ if mode == ProbabilisticBooleanMode.STRICT:
59
58
  return val == ProbabilisticBooleanModeStrict
60
59
  if mode == ProbabilisticBooleanMode.MEDIUM:
61
60
  return val in ProbabilisticBooleanModeMedium
@@ -73,7 +72,9 @@ def _cast_collection_response(rsp: Any, return_constraint: type) -> Any:
73
72
  logger.warning(f"Failed to cast return type to {return_constraint} for {rsp!s}")
74
73
  warnings.warn(f"Failed to cast return type to {return_constraint}", stacklevel=2)
75
74
  res = rsp
76
- assert res is not None, f"Return type cast failed! Check if the return type is correct or post_processors output matches desired format: {rsp!s}"
75
+ assert res is not None, (
76
+ f"Return type cast failed! Check if the return type is correct or post_processors output matches desired format: {rsp!s}"
77
+ )
77
78
  return res
78
79
 
79
80
 
@@ -93,7 +94,9 @@ def _cast_with_fallback(rsp: Any, return_constraint: type) -> Any:
93
94
  return rsp
94
95
 
95
96
 
96
- def _cast_return_type(rsp: Any, return_constraint: type, engine_probabilistic_boolean_mode: ProbabilisticBooleanMode) -> Any:
97
+ def _cast_return_type(
98
+ rsp: Any, return_constraint: type, engine_probabilistic_boolean_mode: ProbabilisticBooleanMode
99
+ ) -> Any:
97
100
  if return_constraint is inspect._empty:
98
101
  return rsp
99
102
  if issubclass(return_constraint, BaseModel):
@@ -109,7 +112,10 @@ def _cast_return_type(rsp: Any, return_constraint: type, engine_probabilistic_bo
109
112
  return _cast_with_fallback(rsp, return_constraint)
110
113
  return rsp
111
114
 
112
- def _apply_postprocessors(outputs, return_constraint, post_processors, argument, mode=ENGINE_PROBABILISTIC_BOOLEAN_MODE):
115
+
116
+ def _apply_postprocessors(
117
+ outputs, return_constraint, post_processors, argument, mode=ENGINE_PROBABILISTIC_BOOLEAN_MODE
118
+ ):
113
119
  if argument.prop.preview:
114
120
  return outputs
115
121
 
@@ -118,7 +124,7 @@ def _apply_postprocessors(outputs, return_constraint, post_processors, argument,
118
124
  argument.prop.metadata = metadata
119
125
 
120
126
  if argument.prop.raw_output:
121
- return metadata.get('raw_output'), metadata
127
+ return metadata.get("raw_output"), metadata
122
128
 
123
129
  if post_processors:
124
130
  for pp in post_processors:
@@ -127,29 +133,36 @@ def _apply_postprocessors(outputs, return_constraint, post_processors, argument,
127
133
 
128
134
  for constraint in argument.prop.constraints:
129
135
  if not constraint(rsp):
130
- UserMessage(f"Constraint not satisfied for value {rsp!r} with constraint {constraint}", raise_with=ConstraintViolationException)
136
+ UserMessage(
137
+ f"Constraint not satisfied for value {rsp!r} with constraint {constraint}",
138
+ raise_with=ConstraintViolationException,
139
+ )
131
140
  return rsp, metadata
132
141
 
133
142
 
134
143
  def _apply_preprocessors(argument, instance: Any, pre_processors: list[PreProcessor] | None) -> str:
135
- processed_input = ''
144
+ processed_input = ""
136
145
  if pre_processors and not argument.prop.raw_input:
137
146
  argument.prop.instance = instance
138
147
  for pp in pre_processors:
139
148
  t = pp(argument)
140
- processed_input += t if t is not None else ''
149
+ processed_input += t if t is not None else ""
141
150
  else:
142
151
  if argument.args and len(argument.args) > 0:
143
- processed_input += ' '.join([str(a) for a in argument.args])
152
+ processed_input += " ".join([str(a) for a in argument.args])
144
153
  return processed_input
145
154
 
146
155
 
147
156
  def _limit_number_results(rsp: Any, argument, return_type):
148
- limit_ = argument.prop.limit if argument.prop.limit else (len(rsp) if hasattr(rsp, '__len__') else None)
157
+ limit_ = (
158
+ argument.prop.limit
159
+ if argument.prop.limit
160
+ else (len(rsp) if hasattr(rsp, "__len__") else None)
161
+ )
149
162
  # the following line is different from original code to make it work for iterable return types when the limit is 1
150
163
  if limit_ is not None:
151
164
  if return_type is str and isinstance(rsp, list):
152
- return '\n'.join(rsp[:limit_])
165
+ return "\n".join(rsp[:limit_])
153
166
  if return_type is list:
154
167
  return rsp[:limit_]
155
168
  if return_type is dict:
@@ -162,25 +175,38 @@ def _limit_number_results(rsp: Any, argument, return_type):
162
175
  return rsp
163
176
 
164
177
 
165
- def _prepare_argument(argument: Any, engine: Any, instance: Any, func: Callable, constraints: list[Callable], default: Any, limit: int, trials: int, pre_processors: list[PreProcessor] | None, post_processors: list[PostProcessor] | None) -> Any:
178
+ def _prepare_argument(
179
+ argument: Any,
180
+ engine: Any,
181
+ instance: Any,
182
+ func: Callable,
183
+ constraints: list[Callable],
184
+ default: Any,
185
+ limit: int,
186
+ trials: int,
187
+ pre_processors: list[PreProcessor] | None,
188
+ post_processors: list[PostProcessor] | None,
189
+ ) -> Any:
166
190
  # check signature for return type
167
191
  sig = inspect.signature(func)
168
192
  return_constraint = sig._return_annotation
169
- assert 'typing' not in str(return_constraint), "Return type must be of base type not generic Typing object, e.g. int, str, list, etc."
193
+ assert "typing" not in str(return_constraint), (
194
+ "Return type must be of base type not generic Typing object, e.g. int, str, list, etc."
195
+ )
170
196
 
171
197
  # prepare argument container
172
- argument.prop.engine = engine
173
- argument.prop.instance = instance
174
- argument.prop.instance_type = type(instance)
175
- argument.prop.signature = sig
176
- argument.prop.func = func
177
- argument.prop.constraints = constraints
198
+ argument.prop.engine = engine
199
+ argument.prop.instance = instance
200
+ argument.prop.instance_type = type(instance)
201
+ argument.prop.signature = sig
202
+ argument.prop.func = func
203
+ argument.prop.constraints = constraints
178
204
  argument.prop.return_constraint = return_constraint
179
- argument.prop.default = default
180
- argument.prop.limit = limit
181
- argument.prop.trials = trials
182
- argument.prop.pre_processors = pre_processors
183
- argument.prop.post_processors = post_processors
205
+ argument.prop.default = default
206
+ argument.prop.limit = limit
207
+ argument.prop.trials = trials
208
+ argument.prop.pre_processors = pre_processors
209
+ argument.prop.post_processors = post_processors
184
210
  return argument
185
211
 
186
212
 
@@ -209,16 +235,18 @@ def _execute_query_fallback(func, instance, argument, error=None, stack_trace=No
209
235
  raise error from None
210
236
 
211
237
 
212
- def _process_query_single(engine,
213
- instance,
214
- func: Callable,
215
- constraints: list[Callable] | None = None,
216
- default: object | None = None,
217
- limit: int = 1,
218
- trials: int = 1,
219
- pre_processors: list[PreProcessor] | None = None,
220
- post_processors: list[PostProcessor] | None = None,
221
- argument=None):
238
+ def _process_query_single(
239
+ engine,
240
+ instance,
241
+ func: Callable,
242
+ constraints: list[Callable] | None = None,
243
+ default: object | None = None,
244
+ limit: int = 1,
245
+ trials: int = 1,
246
+ pre_processors: list[PreProcessor] | None = None,
247
+ post_processors: list[PostProcessor] | None = None,
248
+ argument=None,
249
+ ):
222
250
  if constraints is None:
223
251
  constraints = []
224
252
  if pre_processors and not isinstance(pre_processors, list):
@@ -226,7 +254,18 @@ def _process_query_single(engine,
226
254
  if post_processors and not isinstance(post_processors, list):
227
255
  post_processors = [post_processors]
228
256
 
229
- argument = _prepare_argument(argument, engine, instance, func, constraints, default, limit, trials, pre_processors, post_processors)
257
+ argument = _prepare_argument(
258
+ argument,
259
+ engine,
260
+ instance,
261
+ func,
262
+ constraints,
263
+ default,
264
+ limit,
265
+ trials,
266
+ pre_processors,
267
+ post_processors,
268
+ )
230
269
 
231
270
  preprocessed_input = _apply_preprocessors(argument, instance, pre_processors)
232
271
  argument.prop.processed_input = preprocessed_input
@@ -237,14 +276,18 @@ def _process_query_single(engine,
237
276
  for _ in range(trials):
238
277
  try:
239
278
  outputs = engine.executor_callback(argument)
240
- result, metadata = _apply_postprocessors(outputs, argument.prop.return_constraint, post_processors, argument)
279
+ result, metadata = _apply_postprocessors(
280
+ outputs, argument.prop.return_constraint, post_processors, argument
281
+ )
241
282
  break
242
283
  except Exception as e:
243
284
  stack_trace = traceback.format_exc()
244
285
  logger.error(f"Failed to execute query: {e!s}")
245
286
  logger.error(f"Stack trace: {stack_trace}")
246
287
  if _ == trials - 1:
247
- result = _execute_query_fallback(func, instance, argument, error=e, stack_trace=stack_trace)
288
+ result = _execute_query_fallback(
289
+ func, instance, argument, error=e, stack_trace=stack_trace
290
+ )
248
291
  if result is None:
249
292
  raise e
250
293
 
@@ -254,8 +297,10 @@ def _process_query_single(engine,
254
297
  return limited_result
255
298
 
256
299
 
257
- def _normalize_processors(pre_processors: list[PreProcessor] | PreProcessor | None,
258
- post_processors: list[PostProcessor] | PostProcessor | None) -> tuple[list[PreProcessor] | None, list[PostProcessor] | None]:
300
+ def _normalize_processors(
301
+ pre_processors: list[PreProcessor] | PreProcessor | None,
302
+ post_processors: list[PostProcessor] | PostProcessor | None,
303
+ ) -> tuple[list[PreProcessor] | None, list[PostProcessor] | None]:
259
304
  if pre_processors and not isinstance(pre_processors, list):
260
305
  pre_processors = [pre_processors]
261
306
  if post_processors and not isinstance(post_processors, list):
@@ -264,14 +309,14 @@ def _normalize_processors(pre_processors: list[PreProcessor] | PreProcessor | No
264
309
 
265
310
 
266
311
  def _run_query_with_retries(
267
- engine: Engine,
268
- argument: Any,
269
- func: Callable,
270
- instance: Any,
271
- trials: int,
272
- return_constraint: type,
273
- post_processors: list[PostProcessor] | None,
274
- ) -> tuple[Any, Any]:
312
+ engine: Engine,
313
+ argument: Any,
314
+ func: Callable,
315
+ instance: Any,
316
+ trials: int,
317
+ return_constraint: type,
318
+ post_processors: list[PostProcessor] | None,
319
+ ) -> tuple[Any, Any]:
275
320
  try_cnt = 0
276
321
  rsp = None
277
322
  metadata = None
@@ -279,7 +324,9 @@ def _run_query_with_retries(
279
324
  try_cnt += 1
280
325
  try:
281
326
  outputs = _execute_query(engine, argument)
282
- rsp, metadata = _apply_postprocessors(outputs, return_constraint, post_processors, argument)
327
+ rsp, metadata = _apply_postprocessors(
328
+ outputs, return_constraint, post_processors, argument
329
+ )
283
330
  break
284
331
  except Exception as error:
285
332
  stack_trace = traceback.format_exc()
@@ -287,7 +334,9 @@ def _run_query_with_retries(
287
334
  logger.error(f"Stack trace: {stack_trace}")
288
335
  if try_cnt < trials:
289
336
  continue
290
- rsp = _execute_query_fallback(func, instance, argument, error=error, stack_trace=stack_trace)
337
+ rsp = _execute_query_fallback(
338
+ func, instance, argument, error=error, stack_trace=stack_trace
339
+ )
291
340
  metadata = None
292
341
  return rsp, metadata
293
342
 
@@ -297,34 +346,46 @@ def _execute_query(engine, argument) -> list[object]:
297
346
  engine.prepare(argument)
298
347
  if argument.prop.preview:
299
348
  return engine.preview(argument)
300
- return engine(argument) # currently only supports single query
349
+ return engine(argument) # currently only supports single query
301
350
 
302
351
 
303
352
  def _process_query(
304
- engine: Engine,
305
- instance: Any,
306
- func: Callable,
307
- constraints: list[Callable] | None = None,
308
- default: object | None = None,
309
- limit: int | None = None,
310
- trials: int = 1,
311
- pre_processors: list[PreProcessor] | None = None,
312
- post_processors: list[PostProcessor] | None = None,
313
- argument: Argument = None,
314
- ) -> Any:
315
-
353
+ engine: Engine,
354
+ instance: Any,
355
+ func: Callable,
356
+ constraints: list[Callable] | None = None,
357
+ default: object | None = None,
358
+ limit: int | None = None,
359
+ trials: int = 1,
360
+ pre_processors: list[PreProcessor] | None = None,
361
+ post_processors: list[PostProcessor] | None = None,
362
+ argument: Argument = None,
363
+ ) -> Any:
316
364
  if constraints is None:
317
365
  constraints = []
318
366
  pre_processors, post_processors = _normalize_processors(pre_processors, post_processors)
319
367
 
320
- argument = _prepare_argument(argument, engine, instance, func, constraints, default, limit, trials, pre_processors, post_processors)
368
+ argument = _prepare_argument(
369
+ argument,
370
+ engine,
371
+ instance,
372
+ func,
373
+ constraints,
374
+ default,
375
+ limit,
376
+ trials,
377
+ pre_processors,
378
+ post_processors,
379
+ )
321
380
  return_constraint = argument.prop.return_constraint
322
381
  # if prep_processors is empty or none this returns an empty string
323
382
  processed_input = _apply_preprocessors(argument, instance, pre_processors)
324
383
  if not argument.prop.raw_input:
325
384
  argument.prop.processed_input = processed_input
326
385
 
327
- rsp, metadata = _run_query_with_retries(engine, argument, func, instance, trials, return_constraint, post_processors)
386
+ rsp, metadata = _run_query_with_retries(
387
+ engine, argument, func, instance, trials, return_constraint, post_processors
388
+ )
328
389
  if argument.prop.preview:
329
390
  if argument.prop.return_metadata:
330
391
  return rsp, metadata
@@ -341,7 +402,7 @@ class EngineRepository:
341
402
  _instance = None
342
403
 
343
404
  def __init__(self):
344
- if '_engines' not in self.__dict__: # ensures _engines is only set once
405
+ if "_engines" not in self.__dict__: # ensures _engines is only set once
345
406
  self._engines: dict[str, Engine] = {}
346
407
 
347
408
  def __new__(cls, *_args, **_kwargs):
@@ -355,31 +416,50 @@ class EngineRepository:
355
416
  self = EngineRepository()
356
417
  # Check if the engine is already registered
357
418
  if id in self._engines and not allow_engine_override:
358
- UserMessage(f"Engine {id} is already registered. Set allow_engine_override to True to override.", raise_with=ValueError)
419
+ UserMessage(
420
+ f"Engine {id} is already registered. Set allow_engine_override to True to override.",
421
+ raise_with=ValueError,
422
+ )
359
423
 
360
424
  self._engines[id] = engine_instance
361
425
 
362
426
  @staticmethod
363
- def register_from_plugin(id: str, plugin: str, selected_engine: str | None = None, allow_engine_override: bool = False, *args, **kwargs) -> None:
427
+ def register_from_plugin(
428
+ id: str,
429
+ plugin: str,
430
+ selected_engine: str | None = None,
431
+ allow_engine_override: bool = False,
432
+ *args,
433
+ **kwargs,
434
+ ) -> None:
364
435
  # Lazy import keeps functional -> imports -> symbol -> core -> functional cycle broken.
365
- from .imports import Import # noqa
436
+ from .imports import Import # noqa
437
+
366
438
  types = Import.load_module_class(plugin)
367
439
  # filter out engine class type
368
440
  engines = [t for t in types if issubclass(t, Engine) and t is not Engine]
369
441
  if len(engines) > 1 and selected_engine is None:
370
- UserMessage(f"Multiple engines found in plugin {plugin}. Please specify the engine to use.", raise_with=ValueError)
442
+ UserMessage(
443
+ f"Multiple engines found in plugin {plugin}. Please specify the engine to use.",
444
+ raise_with=ValueError,
445
+ )
371
446
  if len(engines) > 1 and selected_engine is not None:
372
447
  engine = [e for e in engines if selected_engine in str(e)]
373
448
  if len(engine) <= 0:
374
- UserMessage(f"No engine named {selected_engine} found in plugin {plugin}.", raise_with=ValueError)
449
+ UserMessage(
450
+ f"No engine named {selected_engine} found in plugin {plugin}.",
451
+ raise_with=ValueError,
452
+ )
375
453
  engine = engines[0](*args, **kwargs)
376
454
  EngineRepository.register(id, engine, allow_engine_override=allow_engine_override)
377
455
 
378
456
  @staticmethod
379
- def register_from_package(package: ModuleType, allow_engine_override: bool = False, *args, **kwargs) -> None:
457
+ def register_from_package(
458
+ package: ModuleType, allow_engine_override: bool = False, *args, **kwargs
459
+ ) -> None:
380
460
  self = EngineRepository()
381
461
  # Iterate over all modules in the given package and import them
382
- for _, module_name, _ in pkgutil.iter_modules(package.__path__, package.__name__ + '.'):
462
+ for _, module_name, _ in pkgutil.iter_modules(package.__path__, package.__name__ + "."):
383
463
  module = importlib.import_module(module_name)
384
464
 
385
465
  # Check all classes defined in the module
@@ -387,25 +467,35 @@ class EngineRepository:
387
467
  attribute = getattr(module, attribute_name)
388
468
 
389
469
  # Register class if it is a subclass of Engine (but not Engine itself)
390
- if inspect.isclass(attribute) and issubclass(attribute, Engine) and attribute is not Engine:
470
+ if (
471
+ inspect.isclass(attribute)
472
+ and issubclass(attribute, Engine)
473
+ and attribute is not Engine
474
+ ):
391
475
  try:
392
- instance = attribute(*args, **kwargs) # Create an instance of the engine class
476
+ instance = attribute(
477
+ *args, **kwargs
478
+ ) # Create an instance of the engine class
393
479
  # Assume the class has an 'init' static method to initialize it
394
- engine_id_func_ = getattr(instance, 'id', None)
480
+ engine_id_func_ = getattr(instance, "id", None)
395
481
  if engine_id_func_ is None:
396
- UserMessage(f"Engine {instance!s} does not have an id. Please add a method id() to the class.", raise_with=ValueError)
482
+ UserMessage(
483
+ f"Engine {instance!s} does not have an id. Please add a method id() to the class.",
484
+ raise_with=ValueError,
485
+ )
397
486
  # call engine_() to get the id of the engine
398
487
  id_ = engine_id_func_()
399
488
  # only registered configured engine
400
489
  if id_ != ENGINE_UNREGISTERED:
401
490
  # register new engine
402
- self.register(id_, instance, allow_engine_override=allow_engine_override)
491
+ self.register(
492
+ id_, instance, allow_engine_override=allow_engine_override
493
+ )
403
494
  except Exception as e:
404
495
  logger.error(f"Failed to register engine {attribute!s}: {e!s}")
405
496
 
406
497
  @staticmethod
407
498
  def get(engine_name: str, *_args, **_kwargs):
408
-
409
499
  self = EngineRepository()
410
500
  # First check if we're in the context manager that dynamically changes models
411
501
  if engine_name == "neurosymbolic":
@@ -415,10 +505,13 @@ class EngineRepository:
415
505
 
416
506
  # Otherwise, fallback to normal lookup:
417
507
  if engine_name not in self._engines:
418
- subpackage_name = engine_name.replace('-', '_')
508
+ subpackage_name = engine_name.replace("-", "_")
419
509
  subpackage = importlib.import_module(f"{engines.__package__}.{subpackage_name}", None)
420
510
  if subpackage is None:
421
- UserMessage(f"The symbolicai library does not contain the engine named {engine_name}.", raise_with=ValueError)
511
+ UserMessage(
512
+ f"The symbolicai library does not contain the engine named {engine_name}.",
513
+ raise_with=ValueError,
514
+ )
422
515
  self.register_from_package(subpackage)
423
516
  engine = self._engines.get(engine_name, None)
424
517
  if engine is None:
@@ -435,7 +528,7 @@ class EngineRepository:
435
528
  self = EngineRepository()
436
529
  if isinstance(engines, str):
437
530
  engines = [engines]
438
- if 'all' in engines:
531
+ if "all" in engines:
439
532
  # Call the command function for all registered engines with provided arguments
440
533
  return [engine.command(*args, **kwargs) for name, engine in self._engines.items()]
441
534
  # Call the command function for the engine with provided arguments
@@ -452,7 +545,7 @@ class EngineRepository:
452
545
  self = EngineRepository()
453
546
  engine = self.get(engine)
454
547
  if engine:
455
- engine_allows_batching = getattr(engine, 'allows_batching', False)
548
+ engine_allows_batching = getattr(engine, "allows_batching", False)
456
549
  if engine_allows_batching:
457
550
  return _process_query_single(engine, *args, **kwargs)
458
551
  return _process_query(engine, *args, **kwargs)
@@ -480,14 +573,19 @@ class EngineRepository:
480
573
 
481
574
  # 2) Fallback: walk ONLY current thread frames (legacy behavior)
482
575
  # Keeping DynamicEngine import lazy prevents functional importing components before it finishes loading.
483
- from .components import DynamicEngine # noqa
576
+ from .components import DynamicEngine # noqa
577
+
484
578
  try:
485
579
  frame = sys._getframe()
486
580
  except Exception:
487
581
  return None
488
582
  while frame:
489
583
  try:
490
- locals_copy = frame.f_locals.copy() if hasattr(frame.f_locals, 'copy') else dict(frame.f_locals)
584
+ locals_copy = (
585
+ frame.f_locals.copy()
586
+ if hasattr(frame.f_locals, "copy")
587
+ else dict(frame.f_locals)
588
+ )
491
589
  except Exception:
492
590
  UserMessage(
493
591
  "Unexpected failure copying frame locals while resolving DynamicEngine.",
@@ -495,7 +593,7 @@ class EngineRepository:
495
593
  )
496
594
  locals_copy = {}
497
595
  for value in locals_copy.values():
498
- if isinstance(value, DynamicEngine) and getattr(value, '_entered', False):
596
+ if isinstance(value, DynamicEngine) and getattr(value, "_entered", False):
499
597
  return value.engine_instance
500
598
  frame = frame.f_back
501
599
  return None