symbolicai 1.2.1__py3-none-any.whl → 1.3.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 +1 -1
- symai/backend/engines/search/engine_parallel.py +92 -53
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/METADATA +1 -1
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/RECORD +8 -8
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/WHEEL +0 -0
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/entry_points.txt +0 -0
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {symbolicai-1.2.1.dist-info → symbolicai-1.3.0.dist-info}/top_level.txt +0 -0
symai/__init__.py
CHANGED
|
@@ -74,7 +74,8 @@ class SearchResult(Result):
|
|
|
74
74
|
self._citations: list[Citation] = []
|
|
75
75
|
try:
|
|
76
76
|
results = self._coerce_results(value)
|
|
77
|
-
|
|
77
|
+
task_meta = self._extract_task_metadata(value)
|
|
78
|
+
text, citations = self._build_text_and_citations(results, task_meta=task_meta)
|
|
78
79
|
self._value = text
|
|
79
80
|
self._citations = citations
|
|
80
81
|
except Exception as e:
|
|
@@ -87,13 +88,26 @@ class SearchResult(Result):
|
|
|
87
88
|
results = raw.get("results", []) if isinstance(raw, dict) else getattr(raw, "results", None)
|
|
88
89
|
if not results:
|
|
89
90
|
return []
|
|
90
|
-
coerced
|
|
91
|
+
coerced = []
|
|
91
92
|
for item in results:
|
|
92
93
|
if item is None:
|
|
93
94
|
continue
|
|
94
95
|
coerced.append(_item_to_mapping(item))
|
|
95
96
|
return coerced
|
|
96
97
|
|
|
98
|
+
def _extract_task_metadata(self, raw: Any) -> dict[str, Any] | None:
|
|
99
|
+
if not isinstance(raw, dict):
|
|
100
|
+
return None
|
|
101
|
+
task_output = raw.get("task_output")
|
|
102
|
+
if task_output is None:
|
|
103
|
+
return None
|
|
104
|
+
output_value = task_output.get("output") if isinstance(task_output, dict) else None
|
|
105
|
+
return {
|
|
106
|
+
"reasoning": raw.get("task_reasoning"),
|
|
107
|
+
"answer": output_value,
|
|
108
|
+
"confidence": raw.get("task_confidence"),
|
|
109
|
+
}
|
|
110
|
+
|
|
97
111
|
def _normalize_url(self, url: str) -> str:
|
|
98
112
|
parts = urlsplit(url)
|
|
99
113
|
scheme = parts.scheme.lower() if parts.scheme else "https"
|
|
@@ -139,11 +153,40 @@ class SearchResult(Result):
|
|
|
139
153
|
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
|
|
140
154
|
return cleaned.strip()
|
|
141
155
|
|
|
142
|
-
def _build_text_and_citations(
|
|
143
|
-
|
|
144
|
-
|
|
156
|
+
def _build_text_and_citations(
|
|
157
|
+
self, results: list[dict[str, Any]], *, task_meta: dict[str, Any] | None = None
|
|
158
|
+
):
|
|
159
|
+
pieces = []
|
|
160
|
+
citations = []
|
|
145
161
|
cursor = 0
|
|
146
|
-
|
|
162
|
+
|
|
163
|
+
if task_meta:
|
|
164
|
+
reasoning = task_meta.get("reasoning")
|
|
165
|
+
answer = task_meta.get("answer")
|
|
166
|
+
confidence = task_meta.get("confidence")
|
|
167
|
+
|
|
168
|
+
if reasoning:
|
|
169
|
+
block = f"<reasoning>\n{reasoning}\n</reasoning>"
|
|
170
|
+
pieces.append(block)
|
|
171
|
+
cursor += len(block)
|
|
172
|
+
|
|
173
|
+
if answer:
|
|
174
|
+
if pieces:
|
|
175
|
+
pieces.append("\n\n")
|
|
176
|
+
cursor += 2
|
|
177
|
+
block = f"<answer>\n{answer}\n</answer>"
|
|
178
|
+
pieces.append(block)
|
|
179
|
+
cursor += len(block)
|
|
180
|
+
|
|
181
|
+
if confidence:
|
|
182
|
+
if pieces:
|
|
183
|
+
pieces.append("\n\n")
|
|
184
|
+
cursor += 2
|
|
185
|
+
block = f"<answer_confidence>\n{confidence}\n</answer_confidence>"
|
|
186
|
+
pieces.append(block)
|
|
187
|
+
cursor += len(block)
|
|
188
|
+
|
|
189
|
+
seen_urls = set()
|
|
147
190
|
cid = 1
|
|
148
191
|
separator = "\n\n---\n\n"
|
|
149
192
|
|
|
@@ -158,13 +201,8 @@ class SearchResult(Result):
|
|
|
158
201
|
|
|
159
202
|
title = str(item.get("title") or "") or urlsplit(normalized_url).netloc
|
|
160
203
|
excerpts = item.get("excerpts") or []
|
|
161
|
-
excerpt_parts
|
|
162
|
-
for
|
|
163
|
-
if not isinstance(ex, str):
|
|
164
|
-
continue
|
|
165
|
-
sanitized = self._sanitize_excerpt(ex)
|
|
166
|
-
if sanitized:
|
|
167
|
-
excerpt_parts.append(sanitized)
|
|
204
|
+
excerpt_parts = [self._sanitize_excerpt(ex) for ex in excerpts]
|
|
205
|
+
excerpt_parts = [p for p in excerpt_parts if p]
|
|
168
206
|
if not excerpt_parts:
|
|
169
207
|
continue
|
|
170
208
|
|
|
@@ -255,16 +293,14 @@ class ExtractResult(Result):
|
|
|
255
293
|
super().__init__(value, **kwargs)
|
|
256
294
|
try:
|
|
257
295
|
results = self._coerce_results(value)
|
|
258
|
-
content_parts
|
|
296
|
+
content_parts = []
|
|
259
297
|
for r in results:
|
|
260
|
-
excerpts = r.get("excerpts") or []
|
|
261
298
|
full = r.get("full_content")
|
|
262
|
-
if
|
|
299
|
+
if full is not None:
|
|
263
300
|
content_parts.append(full)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
content_parts.extend([s for s in excerpts if isinstance(s, str)])
|
|
301
|
+
else:
|
|
302
|
+
excerpts = r.get("excerpts") or []
|
|
303
|
+
content_parts.extend(excerpts)
|
|
268
304
|
self._value = "\n\n".join(content_parts)
|
|
269
305
|
except Exception as e:
|
|
270
306
|
self._value = None
|
|
@@ -276,7 +312,7 @@ class ExtractResult(Result):
|
|
|
276
312
|
results = raw.get("results", []) if isinstance(raw, dict) else getattr(raw, "results", None)
|
|
277
313
|
if not results:
|
|
278
314
|
return []
|
|
279
|
-
coerced
|
|
315
|
+
coerced = []
|
|
280
316
|
for item in results:
|
|
281
317
|
if item is None:
|
|
282
318
|
continue
|
|
@@ -344,8 +380,8 @@ class ParallelEngine(Engine):
|
|
|
344
380
|
def _normalize_include_domains(self, domains: list[str] | None) -> list[str]:
|
|
345
381
|
if not isinstance(domains, list):
|
|
346
382
|
return []
|
|
347
|
-
seen
|
|
348
|
-
out
|
|
383
|
+
seen = set()
|
|
384
|
+
out = []
|
|
349
385
|
for d in domains:
|
|
350
386
|
netloc = self._extract_netloc(d)
|
|
351
387
|
if not netloc or netloc in seen:
|
|
@@ -361,8 +397,8 @@ class ParallelEngine(Engine):
|
|
|
361
397
|
def _normalize_exclude_domains(self, domains: list[str] | None) -> list[str]:
|
|
362
398
|
if not isinstance(domains, list):
|
|
363
399
|
return []
|
|
364
|
-
seen
|
|
365
|
-
out
|
|
400
|
+
seen = set()
|
|
401
|
+
out = []
|
|
366
402
|
for d in domains:
|
|
367
403
|
netloc = self._extract_netloc(d)
|
|
368
404
|
if not netloc or netloc in seen:
|
|
@@ -382,7 +418,7 @@ class ParallelEngine(Engine):
|
|
|
382
418
|
text = value.strip()
|
|
383
419
|
return [text] if text else []
|
|
384
420
|
if isinstance(value, list):
|
|
385
|
-
cleaned
|
|
421
|
+
cleaned = []
|
|
386
422
|
for item in value:
|
|
387
423
|
if item is None:
|
|
388
424
|
continue
|
|
@@ -429,7 +465,7 @@ class ParallelEngine(Engine):
|
|
|
429
465
|
excerpts = {"max_chars_per_result": max_chars_per_result}
|
|
430
466
|
include = self._normalize_include_domains(kwargs.get("allowed_domains"))
|
|
431
467
|
exclude = self._normalize_exclude_domains(kwargs.get("excluded_domains"))
|
|
432
|
-
source_policy
|
|
468
|
+
source_policy = None
|
|
433
469
|
if include or exclude:
|
|
434
470
|
source_policy = {}
|
|
435
471
|
if include:
|
|
@@ -457,7 +493,7 @@ class ParallelEngine(Engine):
|
|
|
457
493
|
|
|
458
494
|
include = self._normalize_include_domains(kwargs.get("allowed_domains"))
|
|
459
495
|
exclude = self._normalize_exclude_domains(kwargs.get("excluded_domains"))
|
|
460
|
-
source_policy
|
|
496
|
+
source_policy = None
|
|
461
497
|
if include or exclude:
|
|
462
498
|
source_policy = {}
|
|
463
499
|
if include:
|
|
@@ -542,7 +578,7 @@ class ParallelEngine(Engine):
|
|
|
542
578
|
source_policy: dict[str, Any] | None,
|
|
543
579
|
task_spec: Any,
|
|
544
580
|
):
|
|
545
|
-
task_kwargs
|
|
581
|
+
task_kwargs = {
|
|
546
582
|
"input": task_input,
|
|
547
583
|
"processor": processor,
|
|
548
584
|
}
|
|
@@ -559,7 +595,7 @@ class ParallelEngine(Engine):
|
|
|
559
595
|
UserMessage(f"Failed to create Parallel task: {e}", raise_with=ValueError)
|
|
560
596
|
|
|
561
597
|
def _fetch_task_result(self, run_id: str, *, timeout: Any, api_timeout: int | None):
|
|
562
|
-
result_kwargs
|
|
598
|
+
result_kwargs = {}
|
|
563
599
|
if api_timeout is not None:
|
|
564
600
|
result_kwargs["api_timeout"] = api_timeout
|
|
565
601
|
if timeout is not None:
|
|
@@ -570,36 +606,40 @@ class ParallelEngine(Engine):
|
|
|
570
606
|
UserMessage(f"Failed to fetch Parallel task result: {e}", raise_with=ValueError)
|
|
571
607
|
|
|
572
608
|
def _task_result_to_search_payload(self, task_result: Any) -> dict[str, Any]:
|
|
573
|
-
payload
|
|
574
|
-
output =
|
|
609
|
+
payload = {"results": []}
|
|
610
|
+
output = task_result.output
|
|
575
611
|
if output is None:
|
|
576
612
|
return payload
|
|
577
613
|
|
|
578
|
-
basis_items =
|
|
614
|
+
basis_items = output.basis or []
|
|
579
615
|
for idx, basis in enumerate(basis_items):
|
|
580
616
|
payload["results"].extend(self._basis_to_results(basis, basis_index=idx))
|
|
581
617
|
|
|
582
618
|
if not payload["results"]:
|
|
583
619
|
payload["results"].append(self._task_fallback_result(output, basis_items))
|
|
584
620
|
|
|
585
|
-
payload["task_output"] =
|
|
586
|
-
payload["task_output_type"] =
|
|
621
|
+
payload["task_output"] = output.content
|
|
622
|
+
payload["task_output_type"] = output.type
|
|
623
|
+
|
|
624
|
+
if basis_items:
|
|
625
|
+
first_basis = basis_items[0]
|
|
626
|
+
payload["task_reasoning"] = first_basis.reasoning
|
|
627
|
+
payload["task_confidence"] = first_basis.confidence
|
|
628
|
+
|
|
587
629
|
return payload
|
|
588
630
|
|
|
589
631
|
def _basis_to_results(self, basis: Any, *, basis_index: int) -> list[dict[str, Any]]:
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
raw_field = getattr(basis, "field", "") or ""
|
|
593
|
-
field_title = raw_field if isinstance(raw_field, str) else str(raw_field)
|
|
632
|
+
reasoning = basis.reasoning or ""
|
|
633
|
+
field_title = basis.field or ""
|
|
594
634
|
if not field_title.strip():
|
|
595
635
|
field_title = "Parallel Task Output"
|
|
596
|
-
citations =
|
|
636
|
+
citations = basis.citations or []
|
|
597
637
|
if not citations:
|
|
598
638
|
if not reasoning:
|
|
599
639
|
return []
|
|
600
640
|
citations = [None]
|
|
601
641
|
|
|
602
|
-
results
|
|
642
|
+
results = []
|
|
603
643
|
# Convert field titles to lowercase slugs by swapping non-alphanumerics for hyphens.
|
|
604
644
|
slug = re.sub(r"[^a-z0-9]+", "-", field_title.lower()).strip("-") or "field"
|
|
605
645
|
basis_url = f"parallel://task-output/{basis_index:04d}-{slug}"
|
|
@@ -609,10 +649,9 @@ class ParallelEngine(Engine):
|
|
|
609
649
|
title = field_title
|
|
610
650
|
excerpts = [reasoning]
|
|
611
651
|
else:
|
|
612
|
-
url = str(
|
|
613
|
-
title = str(
|
|
614
|
-
|
|
615
|
-
excerpts = [snippet for snippet in raw_excerpts if isinstance(snippet, str)]
|
|
652
|
+
url = str(citation.url or "")
|
|
653
|
+
title = str(citation.title or field_title)
|
|
654
|
+
excerpts = citation.excerpts or []
|
|
616
655
|
if not excerpts and reasoning:
|
|
617
656
|
excerpts = [reasoning]
|
|
618
657
|
results.append(
|
|
@@ -625,7 +664,7 @@ class ParallelEngine(Engine):
|
|
|
625
664
|
return results
|
|
626
665
|
|
|
627
666
|
def _task_fallback_result(self, output: Any, basis_items: list[Any]) -> dict[str, Any]:
|
|
628
|
-
content =
|
|
667
|
+
content = output.content
|
|
629
668
|
if isinstance(content, str):
|
|
630
669
|
snippet = content
|
|
631
670
|
elif isinstance(content, (dict, list)):
|
|
@@ -633,9 +672,9 @@ class ParallelEngine(Engine):
|
|
|
633
672
|
else:
|
|
634
673
|
snippet = str(content or "")
|
|
635
674
|
if not snippet:
|
|
636
|
-
extra_reasoning
|
|
675
|
+
extra_reasoning = []
|
|
637
676
|
for basis in basis_items:
|
|
638
|
-
raw_value =
|
|
677
|
+
raw_value = basis.reasoning or ""
|
|
639
678
|
if isinstance(raw_value, str):
|
|
640
679
|
extra_reasoning.append(raw_value)
|
|
641
680
|
else:
|
|
@@ -665,13 +704,13 @@ class ParallelEngine(Engine):
|
|
|
665
704
|
def forward(self, argument):
|
|
666
705
|
kwargs = argument.kwargs
|
|
667
706
|
# Route based on presence of URL vs Query
|
|
668
|
-
url =
|
|
707
|
+
url = argument.prop.url or kwargs.get("url")
|
|
669
708
|
if url:
|
|
670
709
|
return self._extract(str(url), kwargs)
|
|
671
710
|
|
|
672
|
-
raw_query =
|
|
711
|
+
raw_query = argument.prop.prepared_input
|
|
673
712
|
if raw_query is None:
|
|
674
|
-
raw_query =
|
|
713
|
+
raw_query = argument.prop.query
|
|
675
714
|
search_queries = self._coerce_search_queries(raw_query)
|
|
676
715
|
if not search_queries:
|
|
677
716
|
UserMessage(
|
|
@@ -685,11 +724,11 @@ class ParallelEngine(Engine):
|
|
|
685
724
|
|
|
686
725
|
def prepare(self, argument):
|
|
687
726
|
# For scraping: store URL directly. For search: pass through query string.
|
|
688
|
-
url = argument.kwargs.get("url") or
|
|
727
|
+
url = argument.kwargs.get("url") or argument.prop.url
|
|
689
728
|
if url:
|
|
690
729
|
argument.prop.prepared_input = str(url)
|
|
691
730
|
return
|
|
692
|
-
query =
|
|
731
|
+
query = argument.prop.query
|
|
693
732
|
if isinstance(query, list):
|
|
694
733
|
argument.prop.prepared_input = self._coerce_search_queries(query)
|
|
695
734
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: symbolicai
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A Neurosymbolic Perspective on Large Language Models
|
|
5
5
|
Author-email: Marius-Constantin Dinu <marius@extensity.ai>, Leoveanu-Condrei Claudiu <leo@extensity.ai>
|
|
6
6
|
License: BSD 3-Clause License
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
symai/TERMS_OF_SERVICE.md,sha256=HN42UXVI_wAVDHjMShzy_k7xAsbjXaATNeMKcIte_eg,91409
|
|
2
|
-
symai/__init__.py,sha256=
|
|
2
|
+
symai/__init__.py,sha256=Kdk86d3uR3kr-C7S_niPdsEbvbUu1z8pBTXb4dKe6Zs,18530
|
|
3
3
|
symai/chat.py,sha256=DCEbmZ96wv-eitAVt6-oF6PT3JM3cT59Iy3r2Hucd_M,14100
|
|
4
4
|
symai/components.py,sha256=s10kLvwAOjSBQQohoHGtAIKs0UHHCd_HhiRvMbNtIH0,64685
|
|
5
5
|
symai/constraints.py,sha256=ljjB9p0qK4DrDl_u5G_Y-Y6WAH5ZHANIqLLxRtwcORs,1980
|
|
@@ -61,7 +61,7 @@ symai/backend/engines/ocr/engine_apilayer.py,sha256=UpC3oHBdSM6wlPVqxwMkemBd-Y0R
|
|
|
61
61
|
symai/backend/engines/output/engine_stdout.py,sha256=BWNXACl5U-WYIJnT1pZNwZsTRMzP1XzA0A7o693mmyQ,899
|
|
62
62
|
symai/backend/engines/scrape/engine_requests.py,sha256=yyVFT9JrZ4S6v5U_cykef-tn5iWGl1MAdpqnDaQ70TA,13821
|
|
63
63
|
symai/backend/engines/search/engine_openai.py,sha256=hAEu3vPZzLTvgmNc4BSZDTcNb4ek4xYeOf8xgti2zRs,14248
|
|
64
|
-
symai/backend/engines/search/engine_parallel.py,sha256=
|
|
64
|
+
symai/backend/engines/search/engine_parallel.py,sha256=vhRavd_LStk6grV1aDZiHWfW9v1uDnCLX0BT8smiV84,27008
|
|
65
65
|
symai/backend/engines/search/engine_perplexity.py,sha256=rXnZjMCSiIRuJcNSchE58-f9zWJmYpkKMHONF_XwGnk,4100
|
|
66
66
|
symai/backend/engines/search/engine_serpapi.py,sha256=ZJJBnEDoLjkpxWt_o4vFZanwqojH8ZFBWmWNnEaIbww,3618
|
|
67
67
|
symai/backend/engines/speech_to_text/engine_local_whisper.py,sha256=EOUh2GCeEhZ2Av72i_AZ4NSj9e46Pl7Ft6sIErFy6FI,8387
|
|
@@ -162,9 +162,9 @@ symai/server/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
|
162
162
|
symai/server/huggingface_server.py,sha256=wSAVqFiKQsCu5UB2YYVpxJBhJ7GgQBBfePxNi265yP8,9039
|
|
163
163
|
symai/server/llama_cpp_server.py,sha256=-WPTNB2cbnwtnpES4AtPM__MCasDKl83jr94JGS9tmI,2144
|
|
164
164
|
symai/server/qdrant_server.py,sha256=l4r4rz29c7cO1dapXO0LQ4sHW4WF44keuz7j8v5azMc,9854
|
|
165
|
-
symbolicai-1.
|
|
166
|
-
symbolicai-1.
|
|
167
|
-
symbolicai-1.
|
|
168
|
-
symbolicai-1.
|
|
169
|
-
symbolicai-1.
|
|
170
|
-
symbolicai-1.
|
|
165
|
+
symbolicai-1.3.0.dist-info/licenses/LICENSE,sha256=9vRFudlJ1ghVfra5lcCUIYQCqnZSYcBLjLHbGRsrQCs,1505
|
|
166
|
+
symbolicai-1.3.0.dist-info/METADATA,sha256=7xQnG02ro-9f-haIsGjx5yMXdarGnRDFujSSaLg1gCU,23603
|
|
167
|
+
symbolicai-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
168
|
+
symbolicai-1.3.0.dist-info/entry_points.txt,sha256=JV5sdydIfUZdDF6QBEQHiZHod6XNPjCjpWQrXh7gTAw,261
|
|
169
|
+
symbolicai-1.3.0.dist-info/top_level.txt,sha256=bOoIDfpDIvCQtQgXcwVKJvxAKwsxpxo2IL4z92rNJjw,6
|
|
170
|
+
symbolicai-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|