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.
- symai/__init__.py +198 -134
- symai/backend/base.py +51 -51
- symai/backend/engines/drawing/engine_bfl.py +33 -33
- symai/backend/engines/drawing/engine_gpt_image.py +4 -10
- symai/backend/engines/embedding/engine_llama_cpp.py +50 -35
- symai/backend/engines/embedding/engine_openai.py +22 -16
- symai/backend/engines/execute/engine_python.py +16 -16
- symai/backend/engines/files/engine_io.py +51 -49
- symai/backend/engines/imagecaptioning/engine_blip2.py +27 -23
- symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +53 -46
- symai/backend/engines/index/engine_pinecone.py +116 -88
- symai/backend/engines/index/engine_qdrant.py +1011 -0
- symai/backend/engines/index/engine_vectordb.py +78 -52
- symai/backend/engines/lean/engine_lean4.py +65 -25
- symai/backend/engines/neurosymbolic/__init__.py +28 -28
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +137 -135
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +145 -152
- symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
- symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +75 -49
- symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +199 -155
- symai/backend/engines/neurosymbolic/engine_groq.py +106 -72
- symai/backend/engines/neurosymbolic/engine_huggingface.py +100 -67
- symai/backend/engines/neurosymbolic/engine_llama_cpp.py +121 -93
- symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +213 -132
- symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +180 -137
- symai/backend/engines/ocr/engine_apilayer.py +18 -20
- symai/backend/engines/output/engine_stdout.py +9 -9
- symai/backend/engines/{webscraping → scrape}/engine_requests.py +25 -11
- symai/backend/engines/search/engine_openai.py +95 -83
- symai/backend/engines/search/engine_parallel.py +665 -0
- symai/backend/engines/search/engine_perplexity.py +40 -41
- symai/backend/engines/search/engine_serpapi.py +33 -28
- symai/backend/engines/speech_to_text/engine_local_whisper.py +37 -27
- symai/backend/engines/symbolic/engine_wolframalpha.py +14 -8
- symai/backend/engines/text_to_speech/engine_openai.py +15 -19
- symai/backend/engines/text_vision/engine_clip.py +34 -28
- symai/backend/engines/userinput/engine_console.py +3 -4
- symai/backend/mixin/anthropic.py +48 -40
- symai/backend/mixin/deepseek.py +4 -5
- symai/backend/mixin/google.py +5 -4
- symai/backend/mixin/groq.py +2 -4
- symai/backend/mixin/openai.py +132 -110
- symai/backend/settings.py +14 -14
- symai/chat.py +164 -94
- symai/collect/dynamic.py +13 -11
- symai/collect/pipeline.py +39 -31
- symai/collect/stats.py +109 -69
- symai/components.py +556 -238
- symai/constraints.py +14 -5
- symai/core.py +1495 -1210
- symai/core_ext.py +55 -50
- symai/endpoints/api.py +113 -58
- symai/extended/api_builder.py +22 -17
- symai/extended/arxiv_pdf_parser.py +13 -5
- symai/extended/bibtex_parser.py +8 -4
- symai/extended/conversation.py +88 -69
- symai/extended/document.py +40 -27
- symai/extended/file_merger.py +45 -7
- symai/extended/graph.py +38 -24
- symai/extended/html_style_template.py +17 -11
- symai/extended/interfaces/blip_2.py +1 -1
- symai/extended/interfaces/clip.py +4 -2
- symai/extended/interfaces/console.py +5 -3
- symai/extended/interfaces/dall_e.py +3 -1
- symai/extended/interfaces/file.py +2 -0
- symai/extended/interfaces/flux.py +3 -1
- symai/extended/interfaces/gpt_image.py +15 -6
- symai/extended/interfaces/input.py +2 -1
- symai/extended/interfaces/llava.py +1 -1
- symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
- symai/extended/interfaces/naive_vectordb.py +2 -2
- symai/extended/interfaces/ocr.py +4 -2
- symai/extended/interfaces/openai_search.py +2 -0
- symai/extended/interfaces/parallel.py +30 -0
- symai/extended/interfaces/perplexity.py +2 -0
- symai/extended/interfaces/pinecone.py +6 -4
- symai/extended/interfaces/python.py +2 -0
- symai/extended/interfaces/serpapi.py +2 -0
- symai/extended/interfaces/terminal.py +0 -1
- symai/extended/interfaces/tts.py +2 -1
- symai/extended/interfaces/whisper.py +2 -1
- symai/extended/interfaces/wolframalpha.py +1 -0
- symai/extended/metrics/__init__.py +1 -1
- symai/extended/metrics/similarity.py +5 -2
- symai/extended/os_command.py +31 -22
- symai/extended/packages/symdev.py +39 -34
- symai/extended/packages/sympkg.py +30 -27
- symai/extended/packages/symrun.py +46 -35
- symai/extended/repo_cloner.py +10 -9
- symai/extended/seo_query_optimizer.py +15 -12
- symai/extended/solver.py +104 -76
- symai/extended/summarizer.py +8 -7
- symai/extended/taypan_interpreter.py +10 -9
- symai/extended/vectordb.py +28 -15
- symai/formatter/formatter.py +39 -31
- symai/formatter/regex.py +46 -44
- symai/functional.py +184 -86
- symai/imports.py +85 -51
- symai/interfaces.py +1 -1
- symai/memory.py +33 -24
- symai/menu/screen.py +28 -19
- symai/misc/console.py +27 -27
- symai/misc/loader.py +4 -3
- symai/models/base.py +147 -76
- symai/models/errors.py +1 -1
- symai/ops/__init__.py +1 -1
- symai/ops/measures.py +17 -14
- symai/ops/primitives.py +933 -635
- symai/post_processors.py +28 -24
- symai/pre_processors.py +58 -52
- symai/processor.py +15 -9
- symai/prompts.py +714 -649
- symai/server/huggingface_server.py +115 -32
- symai/server/llama_cpp_server.py +14 -6
- symai/server/qdrant_server.py +206 -0
- symai/shell.py +98 -39
- symai/shellsv.py +307 -223
- symai/strategy.py +135 -81
- symai/symbol.py +276 -225
- symai/utils.py +62 -46
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +19 -9
- symbolicai-1.1.0.dist-info/RECORD +168 -0
- symbolicai-1.0.0.dist-info/RECORD +0 -163
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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_ =
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
173
|
-
argument.prop.instance
|
|
174
|
-
argument.prop.instance_type
|
|
175
|
-
argument.prop.signature
|
|
176
|
-
argument.prop.func
|
|
177
|
-
argument.prop.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
|
|
180
|
-
argument.prop.limit
|
|
181
|
-
argument.prop.trials
|
|
182
|
-
argument.prop.pre_processors
|
|
183
|
-
argument.prop.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(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
258
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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(
|
|
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(
|
|
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)
|
|
349
|
+
return engine(argument) # currently only supports single query
|
|
301
350
|
|
|
302
351
|
|
|
303
352
|
def _process_query(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
470
|
+
if (
|
|
471
|
+
inspect.isclass(attribute)
|
|
472
|
+
and issubclass(attribute, Engine)
|
|
473
|
+
and attribute is not Engine
|
|
474
|
+
):
|
|
391
475
|
try:
|
|
392
|
-
instance = attribute(
|
|
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,
|
|
480
|
+
engine_id_func_ = getattr(instance, "id", None)
|
|
395
481
|
if engine_id_func_ is None:
|
|
396
|
-
UserMessage(
|
|
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(
|
|
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(
|
|
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
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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
|