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.
- 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 +35 -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/__init__.py +4 -0
- symai/backend/mixin/anthropic.py +48 -40
- symai/backend/mixin/cerebras.py +9 -0
- 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 +578 -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.1.dist-info}/METADATA +19 -9
- symbolicai-1.1.1.dist-info/RECORD +169 -0
- symbolicai-1.0.0.dist-info/RECORD +0 -163
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/WHEEL +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/entry_points.txt +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {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(
|
|
17
|
+
logging.getLogger("charset_normalizer").setLevel(logging.ERROR)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ChatBot(Expression):
|
|
21
|
-
_symai_chat: str =
|
|
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 =
|
|
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 =
|
|
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[
|
|
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(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
66
|
-
logger.debug(f
|
|
67
|
-
logger.debug(f
|
|
68
|
-
logger.debug(f
|
|
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 ==
|
|
74
|
-
self.long_term_memory(
|
|
75
|
-
|
|
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
|
|
78
|
-
message = f
|
|
79
|
-
elif do ==
|
|
80
|
-
message = f
|
|
81
|
-
elif do ==
|
|
82
|
-
message = f
|
|
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
|
|
86
|
-
logger.debug(f
|
|
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
|
|
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
|
|
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
|
|
114
|
-
console = f
|
|
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
|
|
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
|
|
144
|
+
that.short_term_memory.store(f"User: {rsp!s}")
|
|
125
145
|
return rsp
|
|
146
|
+
|
|
126
147
|
return CustomInputPostProcessor
|
|
127
148
|
|
|
128
|
-
def _narration(
|
|
129
|
-
|
|
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
|
|
164
|
-
return _func(self).replace(f
|
|
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 =
|
|
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(
|
|
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(
|
|
217
|
+
self._log_verbose("User", usr)
|
|
182
218
|
|
|
183
219
|
ctxt = self._context_from_user(usr)
|
|
184
|
-
self._log_verbose(
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
215
|
-
self.message = self.narrate(
|
|
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
|
|
260
|
+
if "[HELP]" in ctxt:
|
|
221
261
|
reflection = self._extract_reflection(ctxt)
|
|
222
|
-
self.message = self.narrate(f
|
|
262
|
+
self.message = self.narrate(f"{self.name} ", context=reflection)
|
|
223
263
|
return True
|
|
224
|
-
if
|
|
264
|
+
if "[RECALL]" in ctxt:
|
|
225
265
|
reflection = self._extract_reflection(ctxt)
|
|
226
266
|
category = self._extract_category(ctxt)
|
|
227
|
-
self.message = self.narrate(
|
|
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
|
|
273
|
+
if "[DK]" in ctxt:
|
|
230
274
|
reflection = self._extract_reflection(ctxt)
|
|
231
|
-
self.message = self.narrate(
|
|
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
|
|
284
|
+
if "[SYMBOLIC]" in ctxt:
|
|
238
285
|
q = usr.extract("mathematical formula that WolframAlpha can solve")
|
|
239
|
-
rsp = self.interfaces[
|
|
240
|
-
self.message = self.narrate(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
elif
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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(
|
|
327
|
+
q = usr.extract("user query request")
|
|
265
328
|
reflection = self._extract_reflection(ctxt)
|
|
266
|
-
self.message = self.narrate(
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 {
|
|
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,
|
|
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 = [(
|
|
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(
|
|
106
|
+
if attr_value.startswith("[") and attr_value.endswith("]"):
|
|
105
107
|
attr_value = _process_list_value(attr_value)
|
|
106
|
-
elif attr_value.startswith(
|
|
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
|
|
50
|
-
self.uri: str
|
|
51
|
-
self.db_name: str
|
|
52
|
-
self.collection_name: str
|
|
53
|
-
self.client: MongoClient | None
|
|
54
|
-
self.db: Database | None
|
|
55
|
-
self.collection: Collection | 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__(
|
|
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(
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
"forward": forward,
|
|
84
|
+
"engine": engine,
|
|
85
|
+
"metadata": metadata,
|
|
86
|
+
"created_at": datetime.now(),
|
|
87
|
+
"updated_at": datetime.now(),
|
|
86
88
|
}
|
|
87
|
-
try:
|
|
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({
|
|
96
|
-
|
|
97
|
-
def update(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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] = {
|
|
108
|
+
updates: dict[str, Any] = {"updated_at": datetime.now()}
|
|
105
109
|
if forward is not None:
|
|
106
|
-
updates[
|
|
110
|
+
updates["forward"] = forward
|
|
107
111
|
if engine is not None:
|
|
108
|
-
updates[
|
|
112
|
+
updates["engine"] = engine
|
|
109
113
|
if metadata is not None:
|
|
110
|
-
updates[
|
|
114
|
+
updates["metadata"] = metadata
|
|
111
115
|
|
|
112
|
-
return
|
|
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({
|
|
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
|
|
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
|
|
142
|
-
self.db
|
|
149
|
+
self.client = False
|
|
150
|
+
self.db = None
|
|
143
151
|
self.collection = None
|
|
144
152
|
UserMessage(f"[WARN] MongoClient: Connection failed: {e}")
|
|
145
153
|
|