symbolicai 0.21.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 (134) hide show
  1. symai/__init__.py +269 -173
  2. symai/backend/base.py +123 -110
  3. symai/backend/engines/drawing/engine_bfl.py +45 -44
  4. symai/backend/engines/drawing/engine_gpt_image.py +112 -97
  5. symai/backend/engines/embedding/engine_llama_cpp.py +63 -52
  6. symai/backend/engines/embedding/engine_openai.py +25 -21
  7. symai/backend/engines/execute/engine_python.py +19 -18
  8. symai/backend/engines/files/engine_io.py +104 -95
  9. symai/backend/engines/imagecaptioning/engine_blip2.py +28 -24
  10. symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +102 -79
  11. symai/backend/engines/index/engine_pinecone.py +124 -97
  12. symai/backend/engines/index/engine_qdrant.py +1011 -0
  13. symai/backend/engines/index/engine_vectordb.py +84 -56
  14. symai/backend/engines/lean/engine_lean4.py +96 -52
  15. symai/backend/engines/neurosymbolic/__init__.py +41 -13
  16. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +330 -248
  17. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +329 -264
  18. symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
  19. symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +118 -88
  20. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +344 -299
  21. symai/backend/engines/neurosymbolic/engine_groq.py +173 -115
  22. symai/backend/engines/neurosymbolic/engine_huggingface.py +114 -84
  23. symai/backend/engines/neurosymbolic/engine_llama_cpp.py +144 -118
  24. symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +415 -307
  25. symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +394 -231
  26. symai/backend/engines/ocr/engine_apilayer.py +23 -27
  27. symai/backend/engines/output/engine_stdout.py +10 -13
  28. symai/backend/engines/{webscraping → scrape}/engine_requests.py +101 -54
  29. symai/backend/engines/search/engine_openai.py +100 -88
  30. symai/backend/engines/search/engine_parallel.py +665 -0
  31. symai/backend/engines/search/engine_perplexity.py +44 -45
  32. symai/backend/engines/search/engine_serpapi.py +37 -34
  33. symai/backend/engines/speech_to_text/engine_local_whisper.py +54 -51
  34. symai/backend/engines/symbolic/engine_wolframalpha.py +15 -9
  35. symai/backend/engines/text_to_speech/engine_openai.py +20 -26
  36. symai/backend/engines/text_vision/engine_clip.py +39 -37
  37. symai/backend/engines/userinput/engine_console.py +5 -6
  38. symai/backend/mixin/__init__.py +13 -0
  39. symai/backend/mixin/anthropic.py +48 -38
  40. symai/backend/mixin/deepseek.py +6 -5
  41. symai/backend/mixin/google.py +7 -4
  42. symai/backend/mixin/groq.py +2 -4
  43. symai/backend/mixin/openai.py +140 -110
  44. symai/backend/settings.py +87 -20
  45. symai/chat.py +216 -123
  46. symai/collect/__init__.py +7 -1
  47. symai/collect/dynamic.py +80 -70
  48. symai/collect/pipeline.py +67 -51
  49. symai/collect/stats.py +161 -109
  50. symai/components.py +707 -360
  51. symai/constraints.py +24 -12
  52. symai/core.py +1857 -1233
  53. symai/core_ext.py +83 -80
  54. symai/endpoints/api.py +166 -104
  55. symai/extended/.DS_Store +0 -0
  56. symai/extended/__init__.py +46 -12
  57. symai/extended/api_builder.py +29 -21
  58. symai/extended/arxiv_pdf_parser.py +23 -14
  59. symai/extended/bibtex_parser.py +9 -6
  60. symai/extended/conversation.py +156 -126
  61. symai/extended/document.py +50 -30
  62. symai/extended/file_merger.py +57 -14
  63. symai/extended/graph.py +51 -32
  64. symai/extended/html_style_template.py +18 -14
  65. symai/extended/interfaces/blip_2.py +2 -3
  66. symai/extended/interfaces/clip.py +4 -3
  67. symai/extended/interfaces/console.py +9 -1
  68. symai/extended/interfaces/dall_e.py +4 -2
  69. symai/extended/interfaces/file.py +2 -0
  70. symai/extended/interfaces/flux.py +4 -2
  71. symai/extended/interfaces/gpt_image.py +16 -7
  72. symai/extended/interfaces/input.py +2 -1
  73. symai/extended/interfaces/llava.py +1 -2
  74. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +4 -3
  75. symai/extended/interfaces/naive_vectordb.py +9 -10
  76. symai/extended/interfaces/ocr.py +5 -3
  77. symai/extended/interfaces/openai_search.py +2 -0
  78. symai/extended/interfaces/parallel.py +30 -0
  79. symai/extended/interfaces/perplexity.py +2 -0
  80. symai/extended/interfaces/pinecone.py +12 -9
  81. symai/extended/interfaces/python.py +2 -0
  82. symai/extended/interfaces/serpapi.py +3 -1
  83. symai/extended/interfaces/terminal.py +2 -4
  84. symai/extended/interfaces/tts.py +3 -2
  85. symai/extended/interfaces/whisper.py +3 -2
  86. symai/extended/interfaces/wolframalpha.py +2 -1
  87. symai/extended/metrics/__init__.py +11 -1
  88. symai/extended/metrics/similarity.py +14 -13
  89. symai/extended/os_command.py +39 -29
  90. symai/extended/packages/__init__.py +29 -3
  91. symai/extended/packages/symdev.py +51 -43
  92. symai/extended/packages/sympkg.py +41 -35
  93. symai/extended/packages/symrun.py +63 -50
  94. symai/extended/repo_cloner.py +14 -12
  95. symai/extended/seo_query_optimizer.py +15 -13
  96. symai/extended/solver.py +116 -91
  97. symai/extended/summarizer.py +12 -10
  98. symai/extended/taypan_interpreter.py +17 -18
  99. symai/extended/vectordb.py +122 -92
  100. symai/formatter/__init__.py +9 -1
  101. symai/formatter/formatter.py +51 -47
  102. symai/formatter/regex.py +70 -69
  103. symai/functional.py +325 -176
  104. symai/imports.py +190 -147
  105. symai/interfaces.py +57 -28
  106. symai/memory.py +45 -35
  107. symai/menu/screen.py +28 -19
  108. symai/misc/console.py +66 -56
  109. symai/misc/loader.py +8 -5
  110. symai/models/__init__.py +17 -1
  111. symai/models/base.py +395 -236
  112. symai/models/errors.py +1 -2
  113. symai/ops/__init__.py +32 -22
  114. symai/ops/measures.py +24 -25
  115. symai/ops/primitives.py +1149 -731
  116. symai/post_processors.py +58 -50
  117. symai/pre_processors.py +86 -82
  118. symai/processor.py +21 -13
  119. symai/prompts.py +764 -685
  120. symai/server/huggingface_server.py +135 -49
  121. symai/server/llama_cpp_server.py +21 -11
  122. symai/server/qdrant_server.py +206 -0
  123. symai/shell.py +100 -42
  124. symai/shellsv.py +700 -492
  125. symai/strategy.py +630 -346
  126. symai/symbol.py +368 -322
  127. symai/utils.py +100 -78
  128. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +22 -10
  129. symbolicai-1.1.0.dist-info/RECORD +168 -0
  130. symbolicai-0.21.0.dist-info/RECORD +0 -162
  131. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
  132. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
  133. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
  134. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/top_level.txt +0 -0
symai/collect/dynamic.py CHANGED
@@ -1,5 +1,5 @@
1
- import re
2
1
  import ast
2
+ import re
3
3
 
4
4
 
5
5
  class DynamicClass:
@@ -20,7 +20,7 @@ def create_dynamic_class(class_name, **kwargs):
20
20
 
21
21
  def parse_custom_class_instances(s):
22
22
  pattern = r"(\w+)\((.*?)\)"
23
- if type(s) != str:
23
+ if not isinstance(s, str):
24
24
  return s
25
25
  matches = re.finditer(pattern, s)
26
26
 
@@ -28,80 +28,90 @@ def parse_custom_class_instances(s):
28
28
  class_name = match.group(1)
29
29
  class_args = match.group(2)
30
30
  try:
31
- parsed_args = ast.literal_eval(f'{{{class_args}}}')
32
- except:
31
+ parsed_args = ast.literal_eval(f"{{{class_args}}}")
32
+ except (ValueError, SyntaxError):
33
33
  parsed_args = create_object_from_string(class_args)
34
34
  class_instance = create_dynamic_class(class_name, **parsed_args)
35
35
  s = s.replace(match.group(0), repr(class_instance))
36
36
 
37
37
  return s
38
38
 
39
- # TODO: fix to properly parse nested lists and dicts
40
- def create_object_from_string(str_class):
41
- def updated_attributes_process(str_class):
42
- # Regular expression to extract key-value pairs
43
- attr_pattern = r"(\w+)=(\[.*?\]|\{.*?\}|'.*?'|None|\w+)"
44
- attributes = re.findall(attr_pattern, str_class)
45
-
46
- # Create an instance of the dynamic class with initial attributes
47
- updated_attributes = []
48
- # remove string up until 'content='
49
- content = str_class.split('ChatCompletionMessage(content=')[-1].split(", role=")[0][1:-1]
50
- updated_attributes.append(('content', content))
51
- for key, value in attributes:
52
- if key.startswith("'") and key.endswith("'"):
53
- key = key.strip("'")
54
- if key.startswith('"') and key.endswith('"'):
55
- key = key.strip('"')
56
- if value.startswith("'") and value.endswith("'"):
57
- value = value.strip("'")
58
- if value.startswith('"') and value.endswith('"'):
59
- value = value.strip('"')
60
-
61
- if value.startswith('[') and value.endswith(']'):
62
- value = parse_value(value)
63
- dir(value)
64
- if hasattr(value, '__dict__'):
65
- for k in value.__dict__.keys():
66
- v = getattr(value, k)
67
- if type(v) == str:
68
- value[k.strip("'")] = v.strip("'")
69
- elif value.startswith('{') and value.endswith('}'):
70
- value = parse_value(value)
71
- new_value = {}
72
- for k in value.keys():
73
- v = value[k]
74
- if type(v) == str:
75
- v = v.strip("'")
76
- new_value[k.strip("'")] = v
77
- value = new_value
78
- updated_attributes.append((key, value))
79
- return updated_attributes
80
-
81
- def parse_value(value):
82
- try:
83
- value = parse_custom_class_instances(value)
84
- if type(value) != str:
85
- return value
86
- if value.startswith('['):
87
- value = value[1:-1]
88
- values = value.split(',')
89
- return [parse_value(v.strip()) for v in values]
90
- elif value.startswith('{'):
91
- value = value[1:-1]
92
- values = value.split(',')
93
- return {k.strip(): parse_value(v.strip()) for k, v in [v.split(':', 1) for v in values]}
94
- res = ast.literal_eval(value)
95
- if isinstance(res, dict):
96
- return {k: parse_value(v) for k, v in res.items()}
97
- elif isinstance(res, list) or isinstance(res, tuple) or isinstance(res, set):
98
- return [parse_value(v) for v in res]
99
- else:
100
- return res
101
- except:
39
+
40
+ def _strip_quotes(text):
41
+ if not isinstance(text, str):
42
+ return text
43
+ if text.startswith("'") and text.endswith("'"):
44
+ return text.strip("'")
45
+ if text.startswith('"') and text.endswith('"'):
46
+ return text.strip('"')
47
+ return text
48
+
49
+
50
+ def _extract_content(str_class):
51
+ return str_class.split("ChatCompletionMessage(content=")[-1].split(", role=")[0][1:-1]
52
+
53
+
54
+ def _parse_value(value):
55
+ try:
56
+ value = parse_custom_class_instances(value)
57
+ if not isinstance(value, str):
102
58
  return value
59
+ if value.startswith("["):
60
+ inner_values = value[1:-1]
61
+ values = inner_values.split(",")
62
+ return [_parse_value(v.strip()) for v in values]
63
+ if value.startswith("{"):
64
+ inner_values = value[1:-1]
65
+ values = inner_values.split(",")
66
+ return {
67
+ k.strip(): _parse_value(v.strip()) for k, v in [v.split(":", 1) for v in values]
68
+ }
69
+ result = ast.literal_eval(value)
70
+ if isinstance(result, dict):
71
+ return {k: _parse_value(v) for k, v in result.items()}
72
+ if isinstance(result, (list, tuple, set)):
73
+ return [_parse_value(v) for v in result]
74
+ return result
75
+ except (ValueError, SyntaxError):
76
+ return value
77
+
78
+
79
+ def _process_list_value(raw_value):
80
+ parsed_value = _parse_value(raw_value)
81
+ dir(parsed_value)
82
+ if hasattr(parsed_value, "__dict__"):
83
+ for key in parsed_value.__dict__:
84
+ value = getattr(parsed_value, key)
85
+ if isinstance(value, str):
86
+ parsed_value[key.strip("'")] = value.strip("'")
87
+ return parsed_value
88
+
89
+
90
+ def _process_dict_value(raw_value):
91
+ parsed_value = _parse_value(raw_value)
92
+ new_value = {}
93
+ for key, value in parsed_value.items():
94
+ stripped_value = value.strip("'") if isinstance(value, str) else value
95
+ new_value[key.strip("'")] = stripped_value
96
+ return new_value
97
+
98
+
99
+ def _collect_attributes(str_class):
100
+ attr_pattern = r"(\w+)=(\[.*?\]|\{.*?\}|'.*?'|None|\w+)"
101
+ attributes = re.findall(attr_pattern, str_class)
102
+ updated_attributes = [("content", _extract_content(str_class))]
103
+ for key, raw_value in attributes:
104
+ attr_key = _strip_quotes(key)
105
+ attr_value = _strip_quotes(raw_value)
106
+ if attr_value.startswith("[") and attr_value.endswith("]"):
107
+ attr_value = _process_list_value(attr_value)
108
+ elif attr_value.startswith("{") and attr_value.endswith("}"):
109
+ attr_value = _process_dict_value(attr_value)
110
+ updated_attributes.append((attr_key, attr_value))
111
+ return updated_attributes
103
112
 
104
- updated_attributes = updated_attributes_process(str_class)
105
- obj = DynamicClass(**{key: parse_value(value) for key, value in updated_attributes})
106
113
 
107
- return obj
114
+ # TODO: fix to properly parse nested lists and dicts
115
+ def create_object_from_string(str_class):
116
+ updated_attributes = _collect_attributes(str_class)
117
+ return DynamicClass(**{key: _parse_value(value) for key, value in updated_attributes})
symai/collect/pipeline.py CHANGED
@@ -1,14 +1,21 @@
1
+ from __future__ import annotations
2
+
1
3
  import json
2
4
  import logging
3
5
  from datetime import datetime
4
- from typing import Any, Dict, List, Optional
6
+ from typing import TYPE_CHECKING, Any
5
7
 
6
8
  from bson.objectid import ObjectId
7
- from pymongo.collection import Collection
8
- from pymongo.database import Database
9
9
  from pymongo.mongo_client import MongoClient
10
10
 
11
11
  from ..backend.settings import SYMAI_CONFIG
12
+ from ..utils import UserMessage
13
+
14
+ if TYPE_CHECKING:
15
+ from pymongo.collection import Collection
16
+ from pymongo.database import Database
17
+ else:
18
+ Collection = Database = Any
12
19
 
13
20
  logger = logging.getLogger(__name__)
14
21
 
@@ -24,35 +31,36 @@ def rec_serialize(obj):
24
31
  if isinstance(obj, (int, float, bool)):
25
32
  # For simple types, return the string representation directly.
26
33
  return obj
27
- elif isinstance(obj, dict):
34
+ if isinstance(obj, dict):
28
35
  # For dictionaries, serialize each value. Keep keys as strings.
29
36
  return {str(key): rec_serialize(value) for key, value in obj.items()}
30
- elif isinstance(obj, (list, tuple, set)):
37
+ if isinstance(obj, (list, tuple, set)):
31
38
  # For lists, tuples, and sets, serialize each element.
32
39
  return [rec_serialize(elem) for elem in obj]
33
- else:
34
- # Attempt JSON serialization first, then fall back to str(...)
35
- try:
36
- return json.dumps(obj)
37
- except TypeError:
38
- return str(obj)
40
+ # Attempt JSON serialization first, then fall back to str(...)
41
+ try:
42
+ return json.dumps(obj)
43
+ except TypeError:
44
+ return str(obj)
39
45
 
40
46
 
41
47
  class CollectionRepository:
42
48
  def __init__(self) -> None:
43
- self.support_community: bool = SYMAI_CONFIG["SUPPORT_COMMUNITY"]
44
- self.uri: str = SYMAI_CONFIG["COLLECTION_URI"]
45
- self.db_name: str = SYMAI_CONFIG["COLLECTION_DB"]
46
- self.collection_name: str = SYMAI_CONFIG["COLLECTION_STORAGE"]
47
- self.client: Optional[MongoClient] = None
48
- self.db: Optional[Database] = None
49
- self.collection: Optional[Collection] = None
50
-
51
- def __enter__(self) -> 'CollectionRepository':
49
+ self.support_community: bool = SYMAI_CONFIG["SUPPORT_COMMUNITY"]
50
+ self.uri: str = SYMAI_CONFIG["COLLECTION_URI"]
51
+ self.db_name: str = SYMAI_CONFIG["COLLECTION_DB"]
52
+ self.collection_name: str = SYMAI_CONFIG["COLLECTION_STORAGE"]
53
+ self.client: MongoClient | None = None
54
+ self.db: Database | None = None
55
+ self.collection: Collection | None = None
56
+
57
+ def __enter__(self) -> CollectionRepository:
52
58
  self.connect()
53
59
  return self
54
60
 
55
- def __exit__(self, exc_type: Optional[type], exc_val: Optional[Exception], exc_tb: Optional[Any]) -> None:
61
+ def __exit__(
62
+ self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any | None
63
+ ) -> None:
56
64
  self.close()
57
65
 
58
66
  def ping(self) -> bool:
@@ -60,62 +68,70 @@ class CollectionRepository:
60
68
  return False
61
69
  # Send a ping to confirm a successful connection
62
70
  try:
63
- self.client.admin.command('ping')
71
+ self.client.admin.command("ping")
64
72
  return True
65
73
  except Exception as e:
66
- print("Connection failed: " + str(e))
74
+ UserMessage(f"Connection failed: {e}")
67
75
  return False
68
76
 
69
- def add(self, forward: Any, engine: Any, metadata: Dict[str, Any] = {}) -> Any:
77
+ def add(self, forward: Any, engine: Any, metadata: dict[str, Any] | None = None) -> Any:
78
+ if metadata is None:
79
+ metadata = {}
70
80
  if not self.support_community:
71
81
  return None
72
82
  record = {
73
- 'forward': forward,
74
- 'engine': engine,
75
- 'metadata': metadata,
76
- 'created_at': datetime.now(),
77
- 'updated_at': datetime.now()
83
+ "forward": forward,
84
+ "engine": engine,
85
+ "metadata": metadata,
86
+ "created_at": datetime.now(),
87
+ "updated_at": datetime.now(),
78
88
  }
79
- try: # assure that adding a record does never cause a system error
89
+ try: # assure that adding a record does never cause a system error
80
90
  return self.collection.insert_one(record).inserted_id if self.collection else None
81
- except Exception as e:
91
+ except Exception:
82
92
  return None
83
93
 
84
- def get(self, record_id: str) -> Optional[Dict[str, Any]]:
94
+ def get(self, record_id: str) -> dict[str, Any] | None:
85
95
  if not self.support_community:
86
96
  return None
87
- return self.collection.find_one({'_id': ObjectId(record_id)}) if self.collection else None
88
-
89
- def update(self,
90
- record_id: str,
91
- forward: Optional[Any] = None,
92
- engine: Optional[str] = None,
93
- metadata: Optional[Dict[str, Any]] = None) -> Any:
97
+ return self.collection.find_one({"_id": ObjectId(record_id)}) if self.collection else None
98
+
99
+ def update(
100
+ self,
101
+ record_id: str,
102
+ forward: Any | None = None,
103
+ engine: str | None = None,
104
+ metadata: dict[str, Any] | None = None,
105
+ ) -> Any:
94
106
  if not self.support_community:
95
107
  return None
96
- updates: Dict[str, Any] = {'updated_at': datetime.now()}
108
+ updates: dict[str, Any] = {"updated_at": datetime.now()}
97
109
  if forward is not None:
98
- updates['forward'] = forward
110
+ updates["forward"] = forward
99
111
  if engine is not None:
100
- updates['engine'] = engine
112
+ updates["engine"] = engine
101
113
  if metadata is not None:
102
- updates['metadata'] = metadata
114
+ updates["metadata"] = metadata
103
115
 
104
- return self.collection.update_one({'_id': ObjectId(record_id)}, {'$set': updates}) if self.collection else None
116
+ return (
117
+ self.collection.update_one({"_id": ObjectId(record_id)}, {"$set": updates})
118
+ if self.collection
119
+ else None
120
+ )
105
121
 
106
122
  def delete(self, record_id: str) -> Any:
107
123
  if not self.support_community:
108
124
  return None
109
- return self.collection.delete_one({'_id': ObjectId(record_id)}) if self.collection else None
125
+ return self.collection.delete_one({"_id": ObjectId(record_id)}) if self.collection else None
110
126
 
111
- def list(self, filters: Optional[Dict[str, Any]] = None, limit: int = 0) -> List[Dict[str, Any]]:
127
+ def list(self, filters: dict[str, Any] | None = None, limit: int = 0) -> list[dict[str, Any]]:
112
128
  if not self.support_community:
113
129
  return None
114
130
  if filters is None:
115
131
  filters = {}
116
132
  return list(self.collection.find(filters).limit(limit)) if self.collection else []
117
133
 
118
- def count(self, filters: Optional[Dict[str, Any]] = None) -> int:
134
+ def count(self, filters: dict[str, Any] | None = None) -> int:
119
135
  if not self.support_community:
120
136
  return None
121
137
  if filters is None:
@@ -126,14 +142,14 @@ class CollectionRepository:
126
142
  try:
127
143
  if self.client is None and self.support_community:
128
144
  self.client = MongoClient(self.uri)
129
- self.db = self.client[self.db_name]
145
+ self.db = self.client[self.db_name]
130
146
  self.collection = self.db[self.collection_name]
131
147
  except Exception as e:
132
148
  # disable retries
133
- self.client = False
134
- self.db = None
149
+ self.client = False
150
+ self.db = None
135
151
  self.collection = None
136
- print("[WARN] MongoClient: Connection failed: " + str(e))
152
+ UserMessage(f"[WARN] MongoClient: Connection failed: {e}")
137
153
 
138
154
  def close(self) -> None:
139
155
  if self.client is not None: