symbolicai 1.0.0__py3-none-any.whl → 1.1.1__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 (129) 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 +35 -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/__init__.py +4 -0
  39. symai/backend/mixin/anthropic.py +48 -40
  40. symai/backend/mixin/cerebras.py +9 -0
  41. symai/backend/mixin/deepseek.py +4 -5
  42. symai/backend/mixin/google.py +5 -4
  43. symai/backend/mixin/groq.py +2 -4
  44. symai/backend/mixin/openai.py +132 -110
  45. symai/backend/settings.py +14 -14
  46. symai/chat.py +164 -94
  47. symai/collect/dynamic.py +13 -11
  48. symai/collect/pipeline.py +39 -31
  49. symai/collect/stats.py +109 -69
  50. symai/components.py +578 -238
  51. symai/constraints.py +14 -5
  52. symai/core.py +1495 -1210
  53. symai/core_ext.py +55 -50
  54. symai/endpoints/api.py +113 -58
  55. symai/extended/api_builder.py +22 -17
  56. symai/extended/arxiv_pdf_parser.py +13 -5
  57. symai/extended/bibtex_parser.py +8 -4
  58. symai/extended/conversation.py +88 -69
  59. symai/extended/document.py +40 -27
  60. symai/extended/file_merger.py +45 -7
  61. symai/extended/graph.py +38 -24
  62. symai/extended/html_style_template.py +17 -11
  63. symai/extended/interfaces/blip_2.py +1 -1
  64. symai/extended/interfaces/clip.py +4 -2
  65. symai/extended/interfaces/console.py +5 -3
  66. symai/extended/interfaces/dall_e.py +3 -1
  67. symai/extended/interfaces/file.py +2 -0
  68. symai/extended/interfaces/flux.py +3 -1
  69. symai/extended/interfaces/gpt_image.py +15 -6
  70. symai/extended/interfaces/input.py +2 -1
  71. symai/extended/interfaces/llava.py +1 -1
  72. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
  73. symai/extended/interfaces/naive_vectordb.py +2 -2
  74. symai/extended/interfaces/ocr.py +4 -2
  75. symai/extended/interfaces/openai_search.py +2 -0
  76. symai/extended/interfaces/parallel.py +30 -0
  77. symai/extended/interfaces/perplexity.py +2 -0
  78. symai/extended/interfaces/pinecone.py +6 -4
  79. symai/extended/interfaces/python.py +2 -0
  80. symai/extended/interfaces/serpapi.py +2 -0
  81. symai/extended/interfaces/terminal.py +0 -1
  82. symai/extended/interfaces/tts.py +2 -1
  83. symai/extended/interfaces/whisper.py +2 -1
  84. symai/extended/interfaces/wolframalpha.py +1 -0
  85. symai/extended/metrics/__init__.py +1 -1
  86. symai/extended/metrics/similarity.py +5 -2
  87. symai/extended/os_command.py +31 -22
  88. symai/extended/packages/symdev.py +39 -34
  89. symai/extended/packages/sympkg.py +30 -27
  90. symai/extended/packages/symrun.py +46 -35
  91. symai/extended/repo_cloner.py +10 -9
  92. symai/extended/seo_query_optimizer.py +15 -12
  93. symai/extended/solver.py +104 -76
  94. symai/extended/summarizer.py +8 -7
  95. symai/extended/taypan_interpreter.py +10 -9
  96. symai/extended/vectordb.py +28 -15
  97. symai/formatter/formatter.py +39 -31
  98. symai/formatter/regex.py +46 -44
  99. symai/functional.py +184 -86
  100. symai/imports.py +85 -51
  101. symai/interfaces.py +1 -1
  102. symai/memory.py +33 -24
  103. symai/menu/screen.py +28 -19
  104. symai/misc/console.py +27 -27
  105. symai/misc/loader.py +4 -3
  106. symai/models/base.py +147 -76
  107. symai/models/errors.py +1 -1
  108. symai/ops/__init__.py +1 -1
  109. symai/ops/measures.py +17 -14
  110. symai/ops/primitives.py +933 -635
  111. symai/post_processors.py +28 -24
  112. symai/pre_processors.py +58 -52
  113. symai/processor.py +15 -9
  114. symai/prompts.py +714 -649
  115. symai/server/huggingface_server.py +115 -32
  116. symai/server/llama_cpp_server.py +14 -6
  117. symai/server/qdrant_server.py +206 -0
  118. symai/shell.py +98 -39
  119. symai/shellsv.py +307 -223
  120. symai/strategy.py +135 -81
  121. symai/symbol.py +276 -225
  122. symai/utils.py +62 -46
  123. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/METADATA +19 -9
  124. symbolicai-1.1.1.dist-info/RECORD +169 -0
  125. symbolicai-1.0.0.dist-info/RECORD +0 -163
  126. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/WHEEL +0 -0
  127. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/entry_points.txt +0 -0
  128. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/licenses/LICENSE +0 -0
  129. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/top_level.txt +0 -0
symai/chat.py CHANGED
@@ -14,21 +14,21 @@ from .prompts import MemoryCapabilities, SymbiaCapabilities
14
14
  from .symbol import Expression, Symbol
15
15
  from .utils import UserMessage
16
16
 
17
- logging.getLogger('charset_normalizer').setLevel(logging.ERROR)
17
+ logging.getLogger("charset_normalizer").setLevel(logging.ERROR)
18
18
 
19
19
 
20
20
  class ChatBot(Expression):
21
- _symai_chat: str = '''This is a conversation between a chatbot (Symbia:) and a human (User:). The chatbot follows a narrative structure, primarily relying on the provided instructions. It uses the user's input as a conditioning factor to generate its responses. Whenever Symbia retrieves any long-term memories, it checks the user's query and incorporates information from the long-term memory buffer into its response. If the long-term memories cannot provide a suitable answer, Symbia then checks its short-term memory to be aware of the topics discussed in the recent conversation rounds. Your primary task is to reply to the user's question or statement by generating a relevant and contextually appropriate response. Do not focus on filling the scratchpad with narration, long-term memory recall, short-term memory recall, or reflections. Always consider any follow-up questions or relevant information from the user to generate a response that is contextually relevant. Endeavor to reply to the greatest possible effort.'''
21
+ _symai_chat: str = """This is a conversation between a chatbot (Symbia:) and a human (User:). The chatbot follows a narrative structure, primarily relying on the provided instructions. It uses the user's input as a conditioning factor to generate its responses. Whenever Symbia retrieves any long-term memories, it checks the user's query and incorporates information from the long-term memory buffer into its response. If the long-term memories cannot provide a suitable answer, Symbia then checks its short-term memory to be aware of the topics discussed in the recent conversation rounds. Your primary task is to reply to the user's question or statement by generating a relevant and contextually appropriate response. Do not focus on filling the scratchpad with narration, long-term memory recall, short-term memory recall, or reflections. Always consider any follow-up questions or relevant information from the user to generate a response that is contextually relevant. Endeavor to reply to the greatest possible effort."""
22
22
 
23
23
  def __init__(
24
24
  self,
25
25
  value: str | None = None,
26
- name: str = 'Symbia',
26
+ name: str = "Symbia",
27
27
  verbose: bool = False,
28
28
  short_term_mem_window_size: int = 10,
29
29
  long_term_mem_top_k: int = 10,
30
- index_name: str = 'symbia_index',
31
- **kwargs
30
+ index_name: str = "symbia_index",
31
+ **kwargs,
32
32
  ):
33
33
  super().__init__(value, **kwargs)
34
34
  self.sym_return_type = ChatBot
@@ -38,57 +38,75 @@ class ChatBot(Expression):
38
38
  self.interfaces = cfg_to_interface()
39
39
  self.short_term_memory = SlidingWindowListMemory(window_size=short_term_mem_window_size)
40
40
  self.long_term_mem_top_k = long_term_mem_top_k
41
- self.long_term_memory = self.interfaces['indexing']
41
+ self.long_term_memory = self.interfaces["indexing"]
42
42
  self._preprocessor = ChatBot._init_custom_input_preprocessor(name=name, that=self)
43
43
  self._postprocessor = ChatBot._init_custom_input_postprocessor(that=self)
44
44
  self.detect_capability = InContextClassification(SymbiaCapabilities())
45
45
  self.detect_memory_usage = InContextClassification(MemoryCapabilities())
46
- self._last_user_input: str = ''
46
+ self._last_user_input: str = ""
47
47
 
48
48
  def repeat(self, query, **_kwargs):
49
- return self.narrate('Symbia does not understand and asks to repeat and give more context.', prompt=query)
50
-
51
- def narrate(self, message: str, context: str | None = None, category: str | None = None, end: bool = False, **kwargs) -> Symbol:
52
- reflection = context if context is not None else ''
53
- ltmem_recall = 'No memories retrieved.'
54
- stmem_recall = '\n'.join(self.short_term_memory.recall())
55
- stmem_recall = stmem_recall if len(stmem_recall) > 0 else 'No memories retrieved.'
56
- ltmem_recall = 'No memories retrieved.'
57
-
58
- if category == 'RECALL':
59
- if (HOME_PATH / 'localdb' / f'{self.index_name}.pkl').exists():
60
- ltmem_recall = '\n'.join(self.long_term_memory(reflection, operation='search', index_name=self.index_name))
49
+ return self.narrate(
50
+ "Symbia does not understand and asks to repeat and give more context.", prompt=query
51
+ )
52
+
53
+ def narrate(
54
+ self,
55
+ message: str,
56
+ context: str | None = None,
57
+ category: str | None = None,
58
+ end: bool = False,
59
+ **kwargs,
60
+ ) -> Symbol:
61
+ reflection = context if context is not None else ""
62
+ ltmem_recall = "No memories retrieved."
63
+ stmem_recall = "\n".join(self.short_term_memory.recall())
64
+ stmem_recall = stmem_recall if len(stmem_recall) > 0 else "No memories retrieved."
65
+ ltmem_recall = "No memories retrieved."
66
+
67
+ if category == "RECALL":
68
+ if (HOME_PATH / "localdb" / f"{self.index_name}.pkl").exists():
69
+ ltmem_recall = "\n".join(
70
+ self.long_term_memory(
71
+ reflection, operation="search", index_name=self.index_name
72
+ )
73
+ )
61
74
  scratchpad = self._memory_scratchpad(reflection, stmem_recall, ltmem_recall)
62
75
  memory_usage = str(self.detect_memory_usage(scratchpad))
63
76
 
64
77
  if self.verbose:
65
- logger.debug(f'Scratchpad:\n{scratchpad}\n')
66
- logger.debug(f'Memory usage:\n{memory_usage}\n')
67
- logger.debug(f'Retrieved from short-term memory:\n{stmem_recall}\n')
68
- logger.debug(f'Retrieved from long-term memory:\n{ltmem_recall}\n')
78
+ logger.debug(f"Scratchpad:\n{scratchpad}\n")
79
+ logger.debug(f"Memory usage:\n{memory_usage}\n")
80
+ logger.debug(f"Retrieved from short-term memory:\n{stmem_recall}\n")
81
+ logger.debug(f"Retrieved from long-term memory:\n{ltmem_recall}\n")
69
82
 
70
83
  do = self._extract_category(memory_usage)
71
84
  reflection = self._extract_reflection(memory_usage)
72
85
 
73
- if do == 'SAVE':
74
- self.long_term_memory(f'{self.name}: {reflection}', operation='add', top_k=self.long_term_mem_top_k, index_name=self.index_name)
75
- self.long_term_memory('save', operation='config', index_name=self.index_name)
86
+ if do == "SAVE":
87
+ self.long_term_memory(
88
+ f"{self.name}: {reflection}",
89
+ operation="add",
90
+ top_k=self.long_term_mem_top_k,
91
+ index_name=self.index_name,
92
+ )
93
+ self.long_term_memory("save", operation="config", index_name=self.index_name)
76
94
  if self.verbose:
77
- logger.debug(f'Store new long-term memory:\n{reflection}\n')
78
- message = f'{self.name} inform the user that the memory was stored.'
79
- elif do == 'DUPLICATE':
80
- message = f'{self.name} engages the user in a conversation about the duplicate topic, showing the user she remembered the past interaction.'
81
- elif do == 'IRRELEVANT':
82
- message = f'{self.name} discusses the topic with the user.'
95
+ logger.debug(f"Store new long-term memory:\n{reflection}\n")
96
+ message = f"{self.name} inform the user that the memory was stored."
97
+ elif do == "DUPLICATE":
98
+ message = f"{self.name} engages the user in a conversation about the duplicate topic, showing the user she remembered the past interaction."
99
+ elif do == "IRRELEVANT":
100
+ message = f"{self.name} discusses the topic with the user."
83
101
 
84
102
  if self.verbose:
85
- logger.debug(f'Storing new short-term memory:\nUser: {self._last_user_input}\n')
86
- logger.debug(f'Storing new short-term memory:\n{self.name}: {reflection}\n')
103
+ logger.debug(f"Storing new short-term memory:\nUser: {self._last_user_input}\n")
104
+ logger.debug(f"Storing new short-term memory:\n{self.name}: {reflection}\n")
87
105
 
88
- reply = f'{self.name}: {self._narration(message, self._last_user_input, reflection, context, ltmem_recall, stmem_recall, **kwargs)}'
106
+ reply = f"{self.name}: {self._narration(message, self._last_user_input, reflection, context, ltmem_recall, stmem_recall, **kwargs)}"
89
107
 
90
108
  if end:
91
- UserMessage(f'\n\n{reply}', text="extensity")
109
+ UserMessage(f"\n\n{reply}", text="extensity")
92
110
 
93
111
  return Symbol(reply)
94
112
 
@@ -96,10 +114,11 @@ class ChatBot(Expression):
96
114
  @core.userinput(
97
115
  pre_processors=[self._preprocessor()],
98
116
  post_processors=[StripPostProcessor(), self._postprocessor()],
99
- **kwargs
117
+ **kwargs,
100
118
  )
101
119
  def _func(_, message) -> str:
102
120
  pass
121
+
103
122
  return Symbol(_func(self, message))
104
123
 
105
124
  @property
@@ -110,23 +129,34 @@ class ChatBot(Expression):
110
129
  def _init_custom_input_preprocessor(name, that):
111
130
  class CustomInputPreProcessor(ConsoleInputPreProcessor):
112
131
  def __call__(self, argument):
113
- msg = re.sub(f'{name}:\s*', '', str(argument.args[0]))
114
- console = f'\n{name}: {msg}\n$> '
132
+ msg = re.sub(f"{name}:\s*", "", str(argument.args[0]))
133
+ console = f"\n{name}: {msg}\n$> "
115
134
  if len(msg) > 0:
116
- that.short_term_memory.store(f'{name}: ' + msg)
135
+ that.short_term_memory.store(f"{name}: " + msg)
117
136
  return console
137
+
118
138
  return CustomInputPreProcessor
119
139
 
120
140
  @staticmethod
121
141
  def _init_custom_input_postprocessor(that):
122
142
  class CustomInputPostProcessor(ConsolePostProcessor):
123
143
  def __call__(self, rsp, _argument):
124
- that.short_term_memory.store(f'User: {rsp!s}')
144
+ that.short_term_memory.store(f"User: {rsp!s}")
125
145
  return rsp
146
+
126
147
  return CustomInputPostProcessor
127
148
 
128
- def _narration(self, msg: str, query: str, reflection: str, context: str, ltmem_recall: str, stmem_recall: str, **kwargs):
129
- prompt = f'''
149
+ def _narration(
150
+ self,
151
+ msg: str,
152
+ query: str,
153
+ reflection: str,
154
+ context: str,
155
+ ltmem_recall: str,
156
+ stmem_recall: str,
157
+ **kwargs,
158
+ ):
159
+ prompt = f"""
130
160
  {self._symai_chat.format(self.name)}
131
161
 
132
162
  [NARRATION](
@@ -155,18 +185,24 @@ class ChatBot(Expression):
155
185
 
156
186
  The chatbot always reply in the following format
157
187
  {self.name}: <reply>
158
- '''
188
+ """
189
+
159
190
  @core.zero_shot(prompt=prompt, **kwargs)
160
191
  def _func(_) -> str:
161
192
  pass
193
+
162
194
  if self.verbose:
163
- logger.debug(f'Narration:\n{prompt}\n')
164
- return _func(self).replace(f'{self.name}: ', '').strip()
195
+ logger.debug(f"Narration:\n{prompt}\n")
196
+ return _func(self).replace(f"{self.name}: ", "").strip()
197
+
165
198
 
166
199
  class SymbiaChat(ChatBot):
167
- def __init__(self, name: str = 'Symbia', verbose: bool = False, **kwargs):
200
+ def __init__(self, name: str = "Symbia", verbose: bool = False, **kwargs):
168
201
  super().__init__(name=name, verbose=verbose, **kwargs)
169
- self.message = self.narrate(f'{self.name} introduces herself, writes a greeting message and asks how to help.', context=None)
202
+ self.message = self.narrate(
203
+ f"{self.name} introduces herself, writes a greeting message and asks how to help.",
204
+ context=None,
205
+ )
170
206
 
171
207
  def forward(self, usr: str | None = None) -> Symbol:
172
208
  loop = True
@@ -178,10 +214,10 @@ class SymbiaChat(ChatBot):
178
214
  while loop:
179
215
  usr, loop = self._resolve_user_input(usr, loop, ask_input)
180
216
  self._last_user_input = usr
181
- self._log_verbose('User', usr)
217
+ self._log_verbose("User", usr)
182
218
 
183
219
  ctxt = self._context_from_user(usr)
184
- self._log_verbose('In-context', ctxt)
220
+ self._log_verbose("In-context", ctxt)
185
221
 
186
222
  if self._handle_exit_context(ctxt):
187
223
  break
@@ -193,7 +229,9 @@ class SymbiaChat(ChatBot):
193
229
 
194
230
  return self.message
195
231
 
196
- def _resolve_user_input(self, usr: Symbol | None, loop: bool, ask_input: bool) -> tuple[Symbol, bool]:
232
+ def _resolve_user_input(
233
+ self, usr: Symbol | None, loop: bool, ask_input: bool
234
+ ) -> tuple[Symbol, bool]:
197
235
  if ask_input:
198
236
  usr = self.input(self.message)
199
237
  else:
@@ -202,87 +240,117 @@ class SymbiaChat(ChatBot):
202
240
 
203
241
  def _log_verbose(self, title: str, content) -> None:
204
242
  if self.verbose:
205
- logger.debug(f'{title}:\n{content}\n')
243
+ logger.debug(f"{title}:\n{content}\n")
206
244
 
207
245
  def _context_from_user(self, usr: Symbol) -> str:
208
246
  text = str(usr)
209
247
  if len(text) == 0:
210
- return '[DK]'
248
+ return "[DK]"
211
249
  return str(self.detect_capability(usr))
212
250
 
213
251
  def _handle_exit_context(self, ctxt: str) -> bool:
214
- if '[EXIT]' in ctxt:
215
- self.message = self.narrate(f'{self.name} writes friendly goodbye message.', context=None, end=True)
252
+ if "[EXIT]" in ctxt:
253
+ self.message = self.narrate(
254
+ f"{self.name} writes friendly goodbye message.", context=None, end=True
255
+ )
216
256
  return True
217
257
  return False
218
258
 
219
259
  def _handle_reflection_context(self, ctxt: str) -> bool:
220
- if '[HELP]' in ctxt:
260
+ if "[HELP]" in ctxt:
221
261
  reflection = self._extract_reflection(ctxt)
222
- self.message = self.narrate(f'{self.name} ', context=reflection)
262
+ self.message = self.narrate(f"{self.name} ", context=reflection)
223
263
  return True
224
- if '[RECALL]' in ctxt:
264
+ if "[RECALL]" in ctxt:
225
265
  reflection = self._extract_reflection(ctxt)
226
266
  category = self._extract_category(ctxt)
227
- self.message = self.narrate(f'{self.name} uses replies based on what has been recovered from the memory.', context=ctxt, category=category)
267
+ self.message = self.narrate(
268
+ f"{self.name} uses replies based on what has been recovered from the memory.",
269
+ context=ctxt,
270
+ category=category,
271
+ )
228
272
  return True
229
- if '[DK]' in ctxt:
273
+ if "[DK]" in ctxt:
230
274
  reflection = self._extract_reflection(ctxt)
231
- self.message = self.narrate(f'{self.name} is not sure about the message and references and asks the user for more context.', context=reflection)
275
+ self.message = self.narrate(
276
+ f"{self.name} is not sure about the message and references and asks the user for more context.",
277
+ context=reflection,
278
+ )
232
279
  return True
233
280
  return False
234
281
 
235
282
  def _handle_interface_context(self, usr: Symbol, ctxt: str) -> None:
236
283
  try:
237
- if '[SYMBOLIC]' in ctxt:
284
+ if "[SYMBOLIC]" in ctxt:
238
285
  q = usr.extract("mathematical formula that WolframAlpha can solve")
239
- rsp = self.interfaces['symbolic'](q)
240
- self.message = self.narrate(f'{self.name} replies to the user and provides the solution of the math problem.', context=rsp)
241
- elif '[SEARCH]' in ctxt:
242
- q = usr.extract('user query request')
243
- rsp = self.interfaces['search'](q)
244
- self.message = self.narrate(f'{self.name} replies to the user based on the online search results.', context=rsp)
245
- elif '[SCRAPER]' in ctxt:
246
- q = usr.extract('URL from text')
247
- q = q.convert('proper URL, example: https://www.google.com')
248
- rsp = self.interfaces['scraper'](q)
249
- self.message = self.narrate(f'{self.name} replies to the user and narrates its findings.', context=rsp)
250
- elif '[SPEECH-TO-TEXT]' in ctxt:
251
- q = usr.extract('extract file path')
252
- rsp = self.interfaces['stt'](q)
253
- self.message = self.narrate(f'{self.name} replies to the user and transcribes the content of the audio file.', context=rsp)
254
- elif '[TEXT-TO-IMAGE]' in ctxt:
255
- q = usr.extract('text for image creation')
256
- rsp = self.interfaces['drawing'](q)
257
- self.message = self.narrate('Symbia replies to the user and provides the image URL.', context=rsp)
258
- elif '[FILE]' in ctxt:
259
- file_path = usr.extract('extract file path')
260
- q = usr.extract('user question')
261
- rsp = self.interfaces['file'](file_path)
262
- self.message = self.narrate(f'{self.name} replies to the user and outlines and relies to the user query.', context=rsp)
286
+ rsp = self.interfaces["symbolic"](q)
287
+ self.message = self.narrate(
288
+ f"{self.name} replies to the user and provides the solution of the math problem.",
289
+ context=rsp,
290
+ )
291
+ elif "[SEARCH]" in ctxt:
292
+ q = usr.extract("user query request")
293
+ rsp = self.interfaces["search"](q)
294
+ self.message = self.narrate(
295
+ f"{self.name} replies to the user based on the online search results.",
296
+ context=rsp,
297
+ )
298
+ elif "[SCRAPER]" in ctxt:
299
+ q = usr.extract("URL from text")
300
+ q = q.convert("proper URL, example: https://www.google.com")
301
+ rsp = self.interfaces["scraper"](q)
302
+ self.message = self.narrate(
303
+ f"{self.name} replies to the user and narrates its findings.", context=rsp
304
+ )
305
+ elif "[SPEECH-TO-TEXT]" in ctxt:
306
+ q = usr.extract("extract file path")
307
+ rsp = self.interfaces["stt"](q)
308
+ self.message = self.narrate(
309
+ f"{self.name} replies to the user and transcribes the content of the audio file.",
310
+ context=rsp,
311
+ )
312
+ elif "[TEXT-TO-IMAGE]" in ctxt:
313
+ q = usr.extract("text for image creation")
314
+ rsp = self.interfaces["drawing"](q)
315
+ self.message = self.narrate(
316
+ "Symbia replies to the user and provides the image URL.", context=rsp
317
+ )
318
+ elif "[FILE]" in ctxt:
319
+ file_path = usr.extract("extract file path")
320
+ q = usr.extract("user question")
321
+ rsp = self.interfaces["file"](file_path)
322
+ self.message = self.narrate(
323
+ f"{self.name} replies to the user and outlines and relies to the user query.",
324
+ context=rsp,
325
+ )
263
326
  else:
264
- q = usr.extract('user query request')
327
+ q = usr.extract("user query request")
265
328
  reflection = self._extract_reflection(ctxt)
266
- self.message = self.narrate(f'{self.name} tries to interpret the response, and if unclear asks the user to restate the statement or add more context.', context=reflection)
329
+ self.message = self.narrate(
330
+ f"{self.name} tries to interpret the response, and if unclear asks the user to restate the statement or add more context.",
331
+ context=reflection,
332
+ )
267
333
 
268
334
  except Exception as e:
269
335
  reflection = self._extract_reflection(ctxt)
270
- self.message = self.narrate(f'{self.name} apologizes and explains the user what went wrong.', context=str(e))
336
+ self.message = self.narrate(
337
+ f"{self.name} apologizes and explains the user what went wrong.", context=str(e)
338
+ )
271
339
 
272
340
  def _extract_reflection(self, msg: str) -> str:
273
- res = re.findall(r'\(([^)]+)\)', msg)
341
+ res = re.findall(r"\(([^)]+)\)", msg)
274
342
  if len(res) > 0:
275
343
  return res.pop()
276
344
  return None
277
345
 
278
346
  def _extract_category(self, msg: str) -> str:
279
- res = re.findall(r'\[([^]]+)\]', msg)
347
+ res = re.findall(r"\[([^]]+)\]", msg)
280
348
  if len(res) > 0:
281
349
  return res.pop()
282
350
  return None
283
351
 
284
352
  def _memory_scratchpad(self, context, short_term_memory, long_term_memory):
285
- return f'''
353
+ return f"""
286
354
  [REFLECT](
287
355
  Query: {self._last_user_input}
288
356
  Reflection: {self._extract_reflection(context)}
@@ -295,11 +363,13 @@ Reflection: {self._extract_reflection(context)}
295
363
  [LONG-TERM MEMORY RECALL](
296
364
  {long_term_memory}
297
365
  )
298
- '''
366
+ """
367
+
299
368
 
300
369
  def run() -> None:
301
370
  chat = SymbiaChat()
302
371
  chat()
303
372
 
304
- if __name__ == '__main__':
373
+
374
+ if __name__ == "__main__":
305
375
  run()
symai/collect/dynamic.py CHANGED
@@ -28,7 +28,7 @@ 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}}}')
31
+ parsed_args = ast.literal_eval(f"{{{class_args}}}")
32
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)
@@ -48,7 +48,7 @@ def _strip_quotes(text):
48
48
 
49
49
 
50
50
  def _extract_content(str_class):
51
- return str_class.split('ChatCompletionMessage(content=')[-1].split(", role=")[0][1:-1]
51
+ return str_class.split("ChatCompletionMessage(content=")[-1].split(", role=")[0][1:-1]
52
52
 
53
53
 
54
54
  def _parse_value(value):
@@ -56,14 +56,16 @@ def _parse_value(value):
56
56
  value = parse_custom_class_instances(value)
57
57
  if not isinstance(value, str):
58
58
  return value
59
- if value.startswith('['):
59
+ if value.startswith("["):
60
60
  inner_values = value[1:-1]
61
- values = inner_values.split(',')
61
+ values = inner_values.split(",")
62
62
  return [_parse_value(v.strip()) for v in values]
63
- if value.startswith('{'):
63
+ if value.startswith("{"):
64
64
  inner_values = value[1:-1]
65
- values = inner_values.split(',')
66
- return {k.strip(): _parse_value(v.strip()) for k, v in [v.split(':', 1) for v in values]}
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
+ }
67
69
  result = ast.literal_eval(value)
68
70
  if isinstance(result, dict):
69
71
  return {k: _parse_value(v) for k, v in result.items()}
@@ -77,7 +79,7 @@ def _parse_value(value):
77
79
  def _process_list_value(raw_value):
78
80
  parsed_value = _parse_value(raw_value)
79
81
  dir(parsed_value)
80
- if hasattr(parsed_value, '__dict__'):
82
+ if hasattr(parsed_value, "__dict__"):
81
83
  for key in parsed_value.__dict__:
82
84
  value = getattr(parsed_value, key)
83
85
  if isinstance(value, str):
@@ -97,13 +99,13 @@ def _process_dict_value(raw_value):
97
99
  def _collect_attributes(str_class):
98
100
  attr_pattern = r"(\w+)=(\[.*?\]|\{.*?\}|'.*?'|None|\w+)"
99
101
  attributes = re.findall(attr_pattern, str_class)
100
- updated_attributes = [('content', _extract_content(str_class))]
102
+ updated_attributes = [("content", _extract_content(str_class))]
101
103
  for key, raw_value in attributes:
102
104
  attr_key = _strip_quotes(key)
103
105
  attr_value = _strip_quotes(raw_value)
104
- if attr_value.startswith('[') and attr_value.endswith(']'):
106
+ if attr_value.startswith("[") and attr_value.endswith("]"):
105
107
  attr_value = _process_list_value(attr_value)
106
- elif attr_value.startswith('{') and attr_value.endswith('}'):
108
+ elif attr_value.startswith("{") and attr_value.endswith("}"):
107
109
  attr_value = _process_dict_value(attr_value)
108
110
  updated_attributes.append((attr_key, attr_value))
109
111
  return updated_attributes
symai/collect/pipeline.py CHANGED
@@ -46,19 +46,21 @@ def rec_serialize(obj):
46
46
 
47
47
  class CollectionRepository:
48
48
  def __init__(self) -> None:
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
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
56
 
57
57
  def __enter__(self) -> CollectionRepository:
58
58
  self.connect()
59
59
  return self
60
60
 
61
- def __exit__(self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any | None) -> None:
61
+ def __exit__(
62
+ self, exc_type: type | None, exc_val: Exception | None, exc_tb: Any | None
63
+ ) -> None:
62
64
  self.close()
63
65
 
64
66
  def ping(self) -> bool:
@@ -66,7 +68,7 @@ class CollectionRepository:
66
68
  return False
67
69
  # Send a ping to confirm a successful connection
68
70
  try:
69
- self.client.admin.command('ping')
71
+ self.client.admin.command("ping")
70
72
  return True
71
73
  except Exception as e:
72
74
  UserMessage(f"Connection failed: {e}")
@@ -78,13 +80,13 @@ class CollectionRepository:
78
80
  if not self.support_community:
79
81
  return None
80
82
  record = {
81
- 'forward': forward,
82
- 'engine': engine,
83
- 'metadata': metadata,
84
- 'created_at': datetime.now(),
85
- 'updated_at': datetime.now()
83
+ "forward": forward,
84
+ "engine": engine,
85
+ "metadata": metadata,
86
+ "created_at": datetime.now(),
87
+ "updated_at": datetime.now(),
86
88
  }
87
- 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
88
90
  return self.collection.insert_one(record).inserted_id if self.collection else None
89
91
  except Exception:
90
92
  return None
@@ -92,29 +94,35 @@ class CollectionRepository:
92
94
  def get(self, record_id: str) -> dict[str, Any] | None:
93
95
  if not self.support_community:
94
96
  return None
95
- return self.collection.find_one({'_id': ObjectId(record_id)}) if self.collection else None
96
-
97
- def update(self,
98
- record_id: str,
99
- forward: Any | None = None,
100
- engine: str | None = None,
101
- metadata: dict[str, Any] | None = 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:
102
106
  if not self.support_community:
103
107
  return None
104
- updates: dict[str, Any] = {'updated_at': datetime.now()}
108
+ updates: dict[str, Any] = {"updated_at": datetime.now()}
105
109
  if forward is not None:
106
- updates['forward'] = forward
110
+ updates["forward"] = forward
107
111
  if engine is not None:
108
- updates['engine'] = engine
112
+ updates["engine"] = engine
109
113
  if metadata is not None:
110
- updates['metadata'] = metadata
114
+ updates["metadata"] = metadata
111
115
 
112
- 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
+ )
113
121
 
114
122
  def delete(self, record_id: str) -> Any:
115
123
  if not self.support_community:
116
124
  return None
117
- 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
118
126
 
119
127
  def list(self, filters: dict[str, Any] | None = None, limit: int = 0) -> list[dict[str, Any]]:
120
128
  if not self.support_community:
@@ -134,12 +142,12 @@ class CollectionRepository:
134
142
  try:
135
143
  if self.client is None and self.support_community:
136
144
  self.client = MongoClient(self.uri)
137
- self.db = self.client[self.db_name]
145
+ self.db = self.client[self.db_name]
138
146
  self.collection = self.db[self.collection_name]
139
147
  except Exception as e:
140
148
  # disable retries
141
- self.client = False
142
- self.db = None
149
+ self.client = False
150
+ self.db = None
143
151
  self.collection = None
144
152
  UserMessage(f"[WARN] MongoClient: Connection failed: {e}")
145
153