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
@@ -0,0 +1,328 @@
1
+ import json
2
+ import logging
3
+ import re
4
+ from copy import deepcopy
5
+
6
+ from cerebras.cloud.sdk import Cerebras
7
+
8
+ from ....components import SelfPrompt
9
+ from ....core_ext import retry
10
+ from ....utils import UserMessage
11
+ from ...base import Engine
12
+ from ...settings import SYMAI_CONFIG
13
+
14
+ logging.getLogger("cerebras").setLevel(logging.ERROR)
15
+ logging.getLogger("requests").setLevel(logging.ERROR)
16
+ logging.getLogger("urllib").setLevel(logging.ERROR)
17
+ logging.getLogger("httpx").setLevel(logging.ERROR)
18
+ logging.getLogger("httpcore").setLevel(logging.ERROR)
19
+
20
+
21
+ _NON_VERBOSE_OUTPUT = (
22
+ "<META_INSTRUCTION/>\n"
23
+ "You do not output anything else, like verbose preambles or post explanation, such as "
24
+ '"Sure, let me...", "Hope that was helpful...", "Yes, I can help you with that...", etc. '
25
+ "Consider well formatted output, e.g. for sentences use punctuation, spaces etc. or for code use "
26
+ "indentation, etc. Never add meta instructions information to your output!\n\n"
27
+ )
28
+
29
+
30
+ class CerebrasEngine(Engine):
31
+ def __init__(self, api_key: str | None = None, model: str | None = None):
32
+ super().__init__()
33
+ self.config = deepcopy(SYMAI_CONFIG)
34
+ # In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
35
+ if api_key is not None and model is not None:
36
+ self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] = api_key
37
+ self.config["NEUROSYMBOLIC_ENGINE_MODEL"] = model
38
+ if self.id() != "neurosymbolic":
39
+ # Do not initialize if not neurosymbolic; avoids conflict with llama.cpp check in
40
+ # EngineRepository.register_from_package.
41
+ return
42
+
43
+ self.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
44
+ self.model = self.config["NEUROSYMBOLIC_ENGINE_MODEL"]
45
+ self.seed = None
46
+ self.name = self.__class__.__name__
47
+
48
+ try:
49
+ self.client = Cerebras(api_key=self.api_key)
50
+ except Exception as exc:
51
+ UserMessage(
52
+ f"Failed to initialize Cerebras client. Please check your Cerebras SDK installation. Caused by: {exc}",
53
+ raise_with=ValueError,
54
+ )
55
+
56
+ def id(self) -> str:
57
+ model_name = self.config.get("NEUROSYMBOLIC_ENGINE_MODEL")
58
+ if model_name and model_name.startswith("cerebras"):
59
+ return "neurosymbolic"
60
+ return super().id()
61
+
62
+ def command(self, *args, **kwargs):
63
+ super().command(*args, **kwargs)
64
+ if "NEUROSYMBOLIC_ENGINE_API_KEY" in kwargs:
65
+ self.api_key = kwargs["NEUROSYMBOLIC_ENGINE_API_KEY"]
66
+ try:
67
+ self.client = Cerebras(api_key=self.api_key)
68
+ except Exception as exc:
69
+ UserMessage(
70
+ f"Failed to reinitialize Cerebras client. Caused by: {exc}",
71
+ raise_with=ValueError,
72
+ )
73
+ if "NEUROSYMBOLIC_ENGINE_MODEL" in kwargs:
74
+ self.model = kwargs["NEUROSYMBOLIC_ENGINE_MODEL"]
75
+ if "seed" in kwargs:
76
+ self.seed = kwargs["seed"]
77
+
78
+ def compute_required_tokens(self, _messages):
79
+ UserMessage(
80
+ "Token counting not implemented for this engine.", raise_with=NotImplementedError
81
+ )
82
+
83
+ def compute_remaining_tokens(self, _prompts: list) -> int:
84
+ UserMessage(
85
+ "Token counting not implemented for this engine.", raise_with=NotImplementedError
86
+ )
87
+
88
+ def _handle_prefix(self, model_name: str) -> str:
89
+ """Handle prefix for model name."""
90
+ return model_name.replace("cerebras:", "")
91
+
92
+ def _extract_thinking_content(self, outputs: list[str]) -> tuple[str | None, list[str]]:
93
+ """Extract thinking content from textual output using <think>...</think> tags if present."""
94
+ if not outputs:
95
+ return None, outputs
96
+
97
+ content = outputs[0]
98
+ if not content:
99
+ return None, outputs
100
+
101
+ # This regular expression matches a <think>...</think> block and captures any content between the tags,
102
+ # including newlines, so that we can separate internal reasoning text from the user-facing answer.
103
+ think_pattern = r"<think>(.*?)</think>"
104
+ match = re.search(think_pattern, content, re.DOTALL)
105
+
106
+ thinking_content = None
107
+ if match:
108
+ thinking_content = match.group(1).strip() or None
109
+
110
+ cleaned_content = re.sub(think_pattern, "", content, flags=re.DOTALL).strip()
111
+ cleaned_outputs = [cleaned_content, *outputs[1:]]
112
+
113
+ return thinking_content, cleaned_outputs
114
+
115
+ # cumulative wait time is < 30s
116
+ @retry(tries=8, delay=0.5, backoff=1.5, max_delay=5, jitter=(0, 0.5))
117
+ def forward(self, argument):
118
+ kwargs = argument.kwargs
119
+ messages = argument.prop.prepared_input
120
+ payload = self._prepare_request_payload(messages, argument)
121
+ except_remedy = kwargs.get("except_remedy")
122
+
123
+ try:
124
+ res = self.client.chat.completions.create(**payload)
125
+ except Exception as exc: # pragma: no cover - defensive path
126
+ res = self._handle_forward_exception(exc, argument, kwargs, except_remedy)
127
+
128
+ return self._build_outputs_and_metadata(res, payload)
129
+
130
+ def _handle_forward_exception(self, exc, argument, kwargs, except_remedy):
131
+ if self.api_key is None or self.api_key == "":
132
+ msg = (
133
+ "Cerebras API key is not set. Please set it in the config file or "
134
+ "pass it as an argument to the command method."
135
+ )
136
+ UserMessage(msg)
137
+ config_key = self.config.get("NEUROSYMBOLIC_ENGINE_API_KEY")
138
+ if config_key is None or config_key == "":
139
+ UserMessage(msg, raise_with=ValueError)
140
+ self.api_key = config_key
141
+ try:
142
+ self.client = Cerebras(api_key=self.api_key)
143
+ except Exception as inner_exc:
144
+ UserMessage(
145
+ f"Failed to initialize Cerebras client after missing API key. Caused by: {inner_exc}",
146
+ raise_with=ValueError,
147
+ )
148
+
149
+ callback = self.client.chat.completions.create
150
+ kwargs["model"] = (
151
+ self._handle_prefix(kwargs["model"])
152
+ if "model" in kwargs
153
+ else self._handle_prefix(self.model)
154
+ )
155
+
156
+ if except_remedy is not None:
157
+ return except_remedy(self, exc, callback, argument)
158
+
159
+ UserMessage(f"Error during generation. Caused by: {exc}", raise_with=ValueError)
160
+ return None
161
+
162
+ def _build_outputs_and_metadata(self, res, payload):
163
+ metadata: dict = {"raw_output": res}
164
+ if payload.get("tools"):
165
+ metadata = self._process_function_calls(res, metadata)
166
+
167
+ outputs: list[str] = []
168
+ thinking_content: str | None = None
169
+
170
+ for choice in res.choices:
171
+ message = choice.message
172
+ outputs.append(getattr(message, "content", "") or "")
173
+ if thinking_content is None:
174
+ reasoning = getattr(message, "reasoning", None)
175
+ if reasoning:
176
+ thinking_content = reasoning
177
+
178
+ if thinking_content is None:
179
+ thinking_content, outputs = self._extract_thinking_content(outputs)
180
+ else:
181
+ _, outputs = self._extract_thinking_content(outputs)
182
+
183
+ if thinking_content:
184
+ metadata["thinking"] = thinking_content
185
+
186
+ return outputs, metadata
187
+
188
+ def _prepare_raw_input(self, argument):
189
+ if not argument.prop.processed_input:
190
+ UserMessage(
191
+ "Need to provide a prompt instruction to the engine if raw_input is enabled.",
192
+ raise_with=ValueError,
193
+ )
194
+ value = argument.prop.processed_input
195
+ if not isinstance(value, list):
196
+ if not isinstance(value, dict):
197
+ value = {"role": "user", "content": str(value)}
198
+ value = [value]
199
+ return value
200
+
201
+ def prepare(self, argument):
202
+ if argument.prop.raw_input:
203
+ argument.prop.prepared_input = self._prepare_raw_input(argument)
204
+ return
205
+ self._validate_response_format(argument)
206
+
207
+ system_message = self._build_system_message(argument)
208
+ user_content = self._build_user_content(argument)
209
+ user_prompt = {"role": "user", "content": user_content}
210
+ system_message, user_prompt = self._apply_self_prompt_if_needed(
211
+ argument, system_message, user_prompt
212
+ )
213
+
214
+ argument.prop.prepared_input = [
215
+ {"role": "system", "content": system_message},
216
+ user_prompt,
217
+ ]
218
+
219
+ def _validate_response_format(self, argument) -> None:
220
+ if argument.prop.response_format:
221
+ response_format = argument.prop.response_format
222
+ assert response_format.get("type") is not None, (
223
+ 'Expected format `{ "type": "json_object" }` for JSON mode. '
224
+ "See Cerebras structured outputs documentation for details."
225
+ )
226
+
227
+ def _build_system_message(self, argument) -> str:
228
+ system_message: str = ""
229
+ if argument.prop.suppress_verbose_output:
230
+ system_message += _NON_VERBOSE_OUTPUT
231
+ if system_message:
232
+ system_message = f"{system_message}\n"
233
+
234
+ ref = argument.prop.instance
235
+ static_context, dynamic_context = ref.global_context
236
+ if len(static_context) > 0:
237
+ system_message += f"<STATIC CONTEXT/>\n{static_context}\n\n"
238
+
239
+ if len(dynamic_context) > 0:
240
+ system_message += f"<DYNAMIC CONTEXT/>\n{dynamic_context}\n\n"
241
+
242
+ if argument.prop.payload:
243
+ system_message += f"<ADDITIONAL CONTEXT/>\n{argument.prop.payload!s}\n\n"
244
+
245
+ examples = argument.prop.examples
246
+ if examples and len(examples) > 0:
247
+ system_message += f"<EXAMPLES/>\n{examples!s}\n\n"
248
+
249
+ if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
250
+ prompt_value = str(argument.prop.prompt)
251
+ system_message += f"<INSTRUCTION/>\n{prompt_value}\n\n"
252
+
253
+ if argument.prop.template_suffix:
254
+ system_message += (
255
+ " You will only generate content for the placeholder "
256
+ f"`{argument.prop.template_suffix!s}` following the instructions and the provided context information.\n\n"
257
+ )
258
+
259
+ return system_message
260
+
261
+ def _build_user_content(self, argument) -> str:
262
+ return str(argument.prop.processed_input)
263
+
264
+ def _apply_self_prompt_if_needed(self, argument, system_message, user_prompt):
265
+ if argument.prop.instance._kwargs.get("self_prompt", False) or argument.prop.self_prompt:
266
+ self_prompter = SelfPrompt()
267
+ result = self_prompter({"user": user_prompt["content"], "system": system_message})
268
+ if result is None:
269
+ UserMessage("Self-prompting failed!", raise_with=ValueError)
270
+ return result["system"], {"role": "user", "content": result["user"]}
271
+ return system_message, user_prompt
272
+
273
+ def _process_function_calls(self, res, metadata):
274
+ hit = False
275
+ if (
276
+ hasattr(res, "choices")
277
+ and res.choices
278
+ and hasattr(res.choices[0], "message")
279
+ and res.choices[0].message
280
+ and hasattr(res.choices[0].message, "tool_calls")
281
+ and res.choices[0].message.tool_calls
282
+ ):
283
+ for tool_call in res.choices[0].message.tool_calls:
284
+ if hasattr(tool_call, "function") and tool_call.function:
285
+ if hit:
286
+ UserMessage(
287
+ "Multiple function calls detected in the response but only the first one will be processed."
288
+ )
289
+ break
290
+ try:
291
+ args_dict = json.loads(tool_call.function.arguments)
292
+ except json.JSONDecodeError:
293
+ args_dict = {}
294
+ metadata["function_call"] = {
295
+ "name": tool_call.function.name,
296
+ "arguments": args_dict,
297
+ }
298
+ hit = True
299
+ return metadata
300
+
301
+ def _prepare_request_payload(self, messages, argument):
302
+ """Prepares the request payload from the argument."""
303
+ kwargs = argument.kwargs
304
+
305
+ n = kwargs.get("n", 1)
306
+ if n > 1:
307
+ UserMessage(
308
+ "If N is supplied, it must be equal to 1. We default to 1 to avoid unexpected batch behavior."
309
+ )
310
+ n = 1
311
+
312
+ response_format = kwargs.get("response_format")
313
+
314
+ return {
315
+ "messages": messages,
316
+ "model": self._handle_prefix(kwargs.get("model", self.model)),
317
+ "max_completion_tokens": kwargs.get("max_completion_tokens"),
318
+ "stop": kwargs.get("stop"),
319
+ "temperature": kwargs.get("temperature", 1),
320
+ "top_p": kwargs.get("top_p", 1),
321
+ "n": n,
322
+ "tools": kwargs.get("tools"),
323
+ "parallel_tool_calls": kwargs.get("parallel_tool_calls"),
324
+ "response_format": response_format,
325
+ "reasoning_effort": kwargs.get("reasoning_effort"),
326
+ "disable_reasoning": kwargs.get("disable_reasoning"),
327
+ "stream": kwargs.get("stream", False),
328
+ }
@@ -1,15 +1,10 @@
1
1
  import logging
2
- import re
3
2
  from copy import deepcopy
4
- from typing import List, Optional
5
3
 
6
- from annotated_types import Not
7
4
  from openai import OpenAI
8
5
 
9
6
  from ....components import SelfPrompt
10
- from ....misc.console import ConsoleStyle
11
- from ....symbol import Symbol
12
- from ....utils import CustomUserWarning, encode_media_frames
7
+ from ....utils import UserMessage
13
8
  from ...base import Engine
14
9
  from ...mixin.deepseek import DeepSeekMixin
15
10
  from ...settings import SYMAI_CONFIG
@@ -22,17 +17,17 @@ logging.getLogger("httpcore").setLevel(logging.ERROR)
22
17
 
23
18
 
24
19
  class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
25
- def __init__(self, api_key: Optional[str] = None, model: Optional[str] = None):
20
+ def __init__(self, api_key: str | None = None, model: str | None = None):
26
21
  super().__init__()
27
22
  self.config = deepcopy(SYMAI_CONFIG)
28
23
  # In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
29
24
  if api_key is not None and model is not None:
30
- self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] = api_key
31
- self.config['NEUROSYMBOLIC_ENGINE_MODEL'] = model
32
- if self.id() != 'neurosymbolic':
33
- return # do not initialize if not neurosymbolic; avoids conflict with llama.cpp check in EngineRepository.register_from_package
34
- self.api_key = self.config['NEUROSYMBOLIC_ENGINE_API_KEY']
35
- self.model = self.config['NEUROSYMBOLIC_ENGINE_MODEL']
25
+ self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] = api_key
26
+ self.config["NEUROSYMBOLIC_ENGINE_MODEL"] = model
27
+ if self.id() != "neurosymbolic":
28
+ return # do not initialize if not neurosymbolic; avoids conflict with llama.cpp check in EngineRepository.register_from_package
29
+ self.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
30
+ self.model = self.config["NEUROSYMBOLIC_ENGINE_MODEL"]
36
31
  self.name = self.__class__.__name__
37
32
  self.tokenizer = None
38
33
  self.max_context_tokens = self.api_max_context_tokens()
@@ -42,95 +37,115 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
42
37
  try:
43
38
  self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
44
39
  except Exception as e:
45
- CustomUserWarning(f'Failed to initialize the DeepSeek client. Please check your library version. Caused by: {e}', raise_with=RuntimeError)
40
+ UserMessage(
41
+ f"Failed to initialize the DeepSeek client. Please check your library version. Caused by: {e}",
42
+ raise_with=RuntimeError,
43
+ )
46
44
 
47
45
  def id(self) -> str:
48
- if self.config.get('NEUROSYMBOLIC_ENGINE_MODEL') and \
49
- self.config.get('NEUROSYMBOLIC_ENGINE_MODEL').startswith('deepseek'):
50
- return 'neurosymbolic'
51
- return super().id() # default to unregistered
46
+ if self.config.get("NEUROSYMBOLIC_ENGINE_MODEL") and self.config.get(
47
+ "NEUROSYMBOLIC_ENGINE_MODEL"
48
+ ).startswith("deepseek"):
49
+ return "neurosymbolic"
50
+ return super().id() # default to unregistered
52
51
 
53
52
  def command(self, *args, **kwargs):
54
53
  super().command(*args, **kwargs)
55
- if 'NEUROSYMBOLIC_ENGINE_API_KEY' in kwargs:
56
- self.api_key = kwargs['NEUROSYMBOLIC_ENGINE_API_KEY']
57
- if 'NEUROSYMBOLIC_ENGINE_MODEL' in kwargs:
58
- self.model = kwargs['NEUROSYMBOLIC_ENGINE_MODEL']
59
- if 'seed' in kwargs:
60
- self.seed = kwargs['seed']
61
-
62
- def compute_required_tokens(self, messages):
63
- CustomUserWarning('Method "compute_required_tokens" not implemented for DeepSeekXReasoningEngine.', raise_with=NotImplementedError)
64
-
65
- def compute_remaining_tokens(self, prompts: list) -> int:
66
- CustomUserWarning('Method "compute_remaining_tokens" not implemented for DeepSeekXReasoningEngine.', raise_with=NotImplementedError)
67
-
68
- def truncate(self, prompts: list[dict], truncation_percentage: float | None, truncation_type: str) -> list[dict]:
69
- CustomUserWarning('Method "truncate" not implemented for DeepSeekXReasoningEngine.', raise_with=NotImplementedError)
54
+ if "NEUROSYMBOLIC_ENGINE_API_KEY" in kwargs:
55
+ self.api_key = kwargs["NEUROSYMBOLIC_ENGINE_API_KEY"]
56
+ if "NEUROSYMBOLIC_ENGINE_MODEL" in kwargs:
57
+ self.model = kwargs["NEUROSYMBOLIC_ENGINE_MODEL"]
58
+ if "seed" in kwargs:
59
+ self.seed = kwargs["seed"]
60
+
61
+ def compute_required_tokens(self, _messages):
62
+ UserMessage(
63
+ 'Method "compute_required_tokens" not implemented for DeepSeekXReasoningEngine.',
64
+ raise_with=NotImplementedError,
65
+ )
66
+
67
+ def compute_remaining_tokens(self, _prompts: list) -> int:
68
+ UserMessage(
69
+ 'Method "compute_remaining_tokens" not implemented for DeepSeekXReasoningEngine.',
70
+ raise_with=NotImplementedError,
71
+ )
72
+
73
+ def truncate(
74
+ self, _prompts: list[dict], _truncation_percentage: float | None, _truncation_type: str
75
+ ) -> list[dict]:
76
+ UserMessage(
77
+ 'Method "truncate" not implemented for DeepSeekXReasoningEngine.',
78
+ raise_with=NotImplementedError,
79
+ )
70
80
 
71
81
  def forward(self, argument):
72
82
  kwargs = argument.kwargs
73
83
  messages = argument.prop.prepared_input
74
84
  payload = self._prepare_request_payload(argument)
75
- except_remedy = kwargs.get('except_remedy')
85
+ except_remedy = kwargs.get("except_remedy")
76
86
 
77
87
  try:
78
88
  res = self.client.chat.completions.create(messages=messages, **payload)
79
89
 
80
90
  except Exception as e:
81
- if self.api_key is None or self.api_key == '':
82
- msg = 'DeepSeek API key is not set. Please set it in the config file or pass it as an argument to the command method.'
83
- logging.error(msg)
84
- if self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] is None or self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] == '':
85
- CustomUserWarning(msg, raise_with=ValueError)
86
- self.api_key = self.config['NEUROSYMBOLIC_ENGINE_API_KEY']
91
+ if self.api_key is None or self.api_key == "":
92
+ msg = "DeepSeek API key is not set. Please set it in the config file or pass it as an argument to the command method."
93
+ UserMessage(msg)
94
+ if (
95
+ self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] is None
96
+ or self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] == ""
97
+ ):
98
+ UserMessage(msg, raise_with=ValueError)
99
+ self.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
87
100
 
88
101
  callback = self.client.chat.completions.create
89
- kwargs['model'] = kwargs['model'] if 'model' in kwargs else self.model
102
+ kwargs["model"] = kwargs.get("model", self.model)
90
103
 
91
104
  if except_remedy is not None:
92
105
  res = except_remedy(self, e, callback, argument)
93
106
  else:
94
- CustomUserWarning(f'Error during generation. Caused by: {e}', raise_with=ValueError)
107
+ UserMessage(f"Error during generation. Caused by: {e}", raise_with=ValueError)
95
108
 
96
109
  reasoning_content = res.choices[0].message.reasoning_content
97
110
  content = res.choices[0].message.content
98
- metadata = {'raw_output': res, 'thinking': reasoning_content}
111
+ metadata = {"raw_output": res, "thinking": reasoning_content}
99
112
 
100
113
  return [content], metadata
101
114
 
102
115
  def _prepare_raw_input(self, argument):
103
116
  if not argument.prop.processed_input:
104
- CustomUserWarning('A prompt instruction is required for DeepSeekXReasoningEngine when raw_input is enabled.', raise_with=ValueError)
117
+ UserMessage(
118
+ "A prompt instruction is required for DeepSeekXReasoningEngine when raw_input is enabled.",
119
+ raise_with=ValueError,
120
+ )
105
121
  value = argument.prop.processed_input
106
122
  # convert to dict if not already
107
- if type(value) != list:
108
- if type(value) != dict:
109
- value = {'role': 'user', 'content': str(value)}
123
+ if not isinstance(value, list):
124
+ if not isinstance(value, dict):
125
+ value = {"role": "user", "content": str(value)}
110
126
  value = [value]
111
127
  return value
112
128
 
113
- def prepare(self, argument):
114
- if argument.prop.raw_input:
115
- argument.prop.prepared_input = self._prepare_raw_input(argument)
116
- return
117
-
129
+ def _build_system_prompt(self, argument):
118
130
  _non_verbose_output = """<META_INSTRUCTION/>\nYou do not output anything else, like verbose preambles or post explanation, such as "Sure, let me...", "Hope that was helpful...", "Yes, I can help you with that...", etc. Consider well formatted output, e.g. for sentences use punctuation, spaces etc. or for code use indentation, etc. Never add meta instructions information to your output!\n\n"""
119
- user: str = ""
120
131
  system: str = ""
132
+ prop = argument.prop
121
133
 
122
- if argument.prop.suppress_verbose_output:
134
+ if prop.suppress_verbose_output:
123
135
  system += _non_verbose_output
124
- system = f'{system}\n' if system and len(system) > 0 else ''
125
-
126
- if argument.prop.response_format:
127
- _rsp_fmt = argument.prop.response_format
128
- if not (_rsp_fmt.get('type') is not None):
129
- CustomUserWarning('Response format type is required! Expected format `{"type": "json_object"}` or other supported types.', raise_with=AssertionError)
136
+ system = f"{system}\n" if system and len(system) > 0 else ""
137
+
138
+ if prop.response_format:
139
+ _rsp_fmt = prop.response_format
140
+ if not (_rsp_fmt.get("type") is not None):
141
+ UserMessage(
142
+ 'Response format type is required! Expected format `{"type": "json_object"}` or other supported types.',
143
+ raise_with=AssertionError,
144
+ )
130
145
  system += _non_verbose_output
131
- system += f'<RESPONSE_FORMAT/>\n{_rsp_fmt["type"]}\n\n'
146
+ system += f"<RESPONSE_FORMAT/>\n{_rsp_fmt['type']}\n\n"
132
147
 
133
- ref = argument.prop.instance
148
+ ref = prop.instance
134
149
  static_ctxt, dyn_ctxt = ref.global_context
135
150
  if len(static_ctxt) > 0:
136
151
  system += f"<STATIC CONTEXT/>\n{static_ctxt}\n\n"
@@ -138,38 +153,53 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
138
153
  if len(dyn_ctxt) > 0:
139
154
  system += f"<DYNAMIC CONTEXT/>\n{dyn_ctxt}\n\n"
140
155
 
141
- payload = argument.prop.payload
142
- if argument.prop.payload:
143
- system += f"<ADDITIONAL CONTEXT/>\n{str(payload)}\n\n"
156
+ payload = prop.payload
157
+ if prop.payload:
158
+ system += f"<ADDITIONAL CONTEXT/>\n{payload!s}\n\n"
144
159
 
145
- examples: List[str] = argument.prop.examples
160
+ examples: list[str] = prop.examples
146
161
  if examples and len(examples) > 0:
147
- system += f"<EXAMPLES/>\n{str(examples)}\n\n"
162
+ system += f"<EXAMPLES/>\n{examples!s}\n\n"
148
163
 
149
- if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
150
- val = str(argument.prop.prompt)
164
+ if prop.prompt is not None and len(prop.prompt) > 0:
165
+ val = str(prop.prompt)
151
166
  system += f"<INSTRUCTION/>\n{val}\n\n"
152
167
 
153
- user += f"{str(argument.prop.processed_input)}"
168
+ if prop.template_suffix:
169
+ system += f" You will only generate content for the placeholder `{prop.template_suffix!s}` following the instructions and the provided context information.\n\n"
154
170
 
155
- if argument.prop.template_suffix:
156
- system += f' You will only generate content for the placeholder `{str(argument.prop.template_suffix)}` following the instructions and the provided context information.\n\n'
171
+ return system
157
172
 
158
- user_prompt = { "role": "user", "content": user }
173
+ def _build_user_prompt(self, argument):
174
+ return {"role": "user", "content": f"{argument.prop.processed_input!s}"}
159
175
 
160
- # First check if the `Symbol` instance has the flag set, otherwise check if it was passed as an argument to a method
161
- if argument.prop.instance._kwargs.get('self_prompt', False) or argument.prop.self_prompt:
176
+ def _apply_self_prompt(self, argument, system, user_prompt):
177
+ prop = argument.prop
178
+ if prop.instance._kwargs.get("self_prompt", False) or prop.self_prompt:
162
179
  self_prompter = SelfPrompt()
163
180
 
164
- res = self_prompter({'user': user, 'system': system})
181
+ res = self_prompter({"user": user_prompt["content"], "system": system})
165
182
  if res is None:
166
- CustomUserWarning("Self-prompting failed for DeepSeekXReasoningEngine.", raise_with=ValueError)
183
+ UserMessage(
184
+ "Self-prompting failed for DeepSeekXReasoningEngine.", raise_with=ValueError
185
+ )
186
+
187
+ user_prompt = {"role": "user", "content": res["user"]}
188
+ system = res["system"]
189
+
190
+ return system, user_prompt
191
+
192
+ def prepare(self, argument):
193
+ if argument.prop.raw_input:
194
+ argument.prop.prepared_input = self._prepare_raw_input(argument)
195
+ return
167
196
 
168
- user_prompt = { "role": "user", "content": res['user'] }
169
- system = res['system']
197
+ system = self._build_system_prompt(argument)
198
+ user_prompt = self._build_user_prompt(argument)
199
+ system, user_prompt = self._apply_self_prompt(argument, system, user_prompt)
170
200
 
171
201
  argument.prop.prepared_input = [
172
- { "role": "system", "content": system },
202
+ {"role": "system", "content": system},
173
203
  user_prompt,
174
204
  ]
175
205
 
@@ -177,13 +207,13 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
177
207
  """Prepares the request payload from the argument."""
178
208
  kwargs = argument.kwargs
179
209
  # 16/03/2025
180
- # Not Supported FeaturesFunction Call、Json Output、FIM (Beta)
181
- # Not Supported Parameterstemperature、top_p、presence_penalty、frequency_penalty、logprobs、top_logprobs
210
+ # Not Supported Features: Function Call、Json Output、FIM (Beta)
211
+ # Not Supported Parameters: temperature、top_p、presence_penalty、frequency_penalty、logprobs、top_logprobs
182
212
  return {
183
- "model": kwargs.get('model', self.model),
184
- "seed": kwargs.get('seed', self.seed),
185
- "max_tokens": kwargs.get('max_tokens', self.max_response_tokens),
186
- "stop": kwargs.get('stop', '<|endoftext|>'),
187
- "n": kwargs.get('n', 1),
188
- "logit_bias": kwargs.get('logit_bias'),
213
+ "model": kwargs.get("model", self.model),
214
+ "seed": kwargs.get("seed", self.seed),
215
+ "max_tokens": kwargs.get("max_tokens", self.max_response_tokens),
216
+ "stop": kwargs.get("stop", "<|endoftext|>"),
217
+ "n": kwargs.get("n", 1),
218
+ "logit_bias": kwargs.get("logit_bias"),
189
219
  }