symbolicai 1.0.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.
- 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 +28 -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/anthropic.py +48 -40
- 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 +556 -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.0.dist-info}/METADATA +19 -9
- symbolicai-1.1.0.dist-info/RECORD +168 -0
- symbolicai-1.0.0.dist-info/RECORD +0 -163
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {symbolicai-1.0.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
|
+
}
|
|
@@ -22,12 +22,12 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
22
22
|
self.config = deepcopy(SYMAI_CONFIG)
|
|
23
23
|
# In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
|
|
24
24
|
if api_key is not None and model is not None:
|
|
25
|
-
self.config[
|
|
26
|
-
self.config[
|
|
27
|
-
if self.id() !=
|
|
28
|
-
return
|
|
29
|
-
self.api_key = self.config[
|
|
30
|
-
self.model = self.config[
|
|
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"]
|
|
31
31
|
self.name = self.__class__.__name__
|
|
32
32
|
self.tokenizer = None
|
|
33
33
|
self.max_context_tokens = self.api_max_context_tokens()
|
|
@@ -37,71 +37,92 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
37
37
|
try:
|
|
38
38
|
self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com")
|
|
39
39
|
except Exception as e:
|
|
40
|
-
UserMessage(
|
|
40
|
+
UserMessage(
|
|
41
|
+
f"Failed to initialize the DeepSeek client. Please check your library version. Caused by: {e}",
|
|
42
|
+
raise_with=RuntimeError,
|
|
43
|
+
)
|
|
41
44
|
|
|
42
45
|
def id(self) -> str:
|
|
43
|
-
if self.config.get(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
47
51
|
|
|
48
52
|
def command(self, *args, **kwargs):
|
|
49
53
|
super().command(*args, **kwargs)
|
|
50
|
-
if
|
|
51
|
-
self.api_key = kwargs[
|
|
52
|
-
if
|
|
53
|
-
self.model = kwargs[
|
|
54
|
-
if
|
|
55
|
-
self.seed = kwargs[
|
|
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"]
|
|
56
60
|
|
|
57
61
|
def compute_required_tokens(self, _messages):
|
|
58
|
-
UserMessage(
|
|
62
|
+
UserMessage(
|
|
63
|
+
'Method "compute_required_tokens" not implemented for DeepSeekXReasoningEngine.',
|
|
64
|
+
raise_with=NotImplementedError,
|
|
65
|
+
)
|
|
59
66
|
|
|
60
67
|
def compute_remaining_tokens(self, _prompts: list) -> int:
|
|
61
|
-
UserMessage(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
+
)
|
|
65
80
|
|
|
66
81
|
def forward(self, argument):
|
|
67
82
|
kwargs = argument.kwargs
|
|
68
83
|
messages = argument.prop.prepared_input
|
|
69
84
|
payload = self._prepare_request_payload(argument)
|
|
70
|
-
except_remedy = kwargs.get(
|
|
85
|
+
except_remedy = kwargs.get("except_remedy")
|
|
71
86
|
|
|
72
87
|
try:
|
|
73
88
|
res = self.client.chat.completions.create(messages=messages, **payload)
|
|
74
89
|
|
|
75
90
|
except Exception as e:
|
|
76
|
-
if self.api_key is None or self.api_key ==
|
|
77
|
-
msg =
|
|
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."
|
|
78
93
|
UserMessage(msg)
|
|
79
|
-
if
|
|
94
|
+
if (
|
|
95
|
+
self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] is None
|
|
96
|
+
or self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] == ""
|
|
97
|
+
):
|
|
80
98
|
UserMessage(msg, raise_with=ValueError)
|
|
81
|
-
self.api_key = self.config[
|
|
99
|
+
self.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
|
|
82
100
|
|
|
83
101
|
callback = self.client.chat.completions.create
|
|
84
|
-
kwargs[
|
|
102
|
+
kwargs["model"] = kwargs.get("model", self.model)
|
|
85
103
|
|
|
86
104
|
if except_remedy is not None:
|
|
87
105
|
res = except_remedy(self, e, callback, argument)
|
|
88
106
|
else:
|
|
89
|
-
UserMessage(f
|
|
107
|
+
UserMessage(f"Error during generation. Caused by: {e}", raise_with=ValueError)
|
|
90
108
|
|
|
91
109
|
reasoning_content = res.choices[0].message.reasoning_content
|
|
92
110
|
content = res.choices[0].message.content
|
|
93
|
-
metadata = {
|
|
111
|
+
metadata = {"raw_output": res, "thinking": reasoning_content}
|
|
94
112
|
|
|
95
113
|
return [content], metadata
|
|
96
114
|
|
|
97
115
|
def _prepare_raw_input(self, argument):
|
|
98
116
|
if not argument.prop.processed_input:
|
|
99
|
-
UserMessage(
|
|
117
|
+
UserMessage(
|
|
118
|
+
"A prompt instruction is required for DeepSeekXReasoningEngine when raw_input is enabled.",
|
|
119
|
+
raise_with=ValueError,
|
|
120
|
+
)
|
|
100
121
|
value = argument.prop.processed_input
|
|
101
122
|
# convert to dict if not already
|
|
102
123
|
if not isinstance(value, list):
|
|
103
124
|
if not isinstance(value, dict):
|
|
104
|
-
value = {
|
|
125
|
+
value = {"role": "user", "content": str(value)}
|
|
105
126
|
value = [value]
|
|
106
127
|
return value
|
|
107
128
|
|
|
@@ -112,14 +133,17 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
112
133
|
|
|
113
134
|
if prop.suppress_verbose_output:
|
|
114
135
|
system += _non_verbose_output
|
|
115
|
-
system = f
|
|
136
|
+
system = f"{system}\n" if system and len(system) > 0 else ""
|
|
116
137
|
|
|
117
138
|
if prop.response_format:
|
|
118
139
|
_rsp_fmt = prop.response_format
|
|
119
|
-
if not (_rsp_fmt.get(
|
|
120
|
-
UserMessage(
|
|
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
|
+
)
|
|
121
145
|
system += _non_verbose_output
|
|
122
|
-
system += f
|
|
146
|
+
system += f"<RESPONSE_FORMAT/>\n{_rsp_fmt['type']}\n\n"
|
|
123
147
|
|
|
124
148
|
ref = prop.instance
|
|
125
149
|
static_ctxt, dyn_ctxt = ref.global_context
|
|
@@ -142,7 +166,7 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
142
166
|
system += f"<INSTRUCTION/>\n{val}\n\n"
|
|
143
167
|
|
|
144
168
|
if prop.template_suffix:
|
|
145
|
-
system += f
|
|
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"
|
|
146
170
|
|
|
147
171
|
return system
|
|
148
172
|
|
|
@@ -151,15 +175,17 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
151
175
|
|
|
152
176
|
def _apply_self_prompt(self, argument, system, user_prompt):
|
|
153
177
|
prop = argument.prop
|
|
154
|
-
if prop.instance._kwargs.get(
|
|
178
|
+
if prop.instance._kwargs.get("self_prompt", False) or prop.self_prompt:
|
|
155
179
|
self_prompter = SelfPrompt()
|
|
156
180
|
|
|
157
|
-
res = self_prompter({
|
|
181
|
+
res = self_prompter({"user": user_prompt["content"], "system": system})
|
|
158
182
|
if res is None:
|
|
159
|
-
UserMessage(
|
|
183
|
+
UserMessage(
|
|
184
|
+
"Self-prompting failed for DeepSeekXReasoningEngine.", raise_with=ValueError
|
|
185
|
+
)
|
|
160
186
|
|
|
161
|
-
user_prompt = {
|
|
162
|
-
system = res[
|
|
187
|
+
user_prompt = {"role": "user", "content": res["user"]}
|
|
188
|
+
system = res["system"]
|
|
163
189
|
|
|
164
190
|
return system, user_prompt
|
|
165
191
|
|
|
@@ -173,7 +199,7 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
173
199
|
system, user_prompt = self._apply_self_prompt(argument, system, user_prompt)
|
|
174
200
|
|
|
175
201
|
argument.prop.prepared_input = [
|
|
176
|
-
{
|
|
202
|
+
{"role": "system", "content": system},
|
|
177
203
|
user_prompt,
|
|
178
204
|
]
|
|
179
205
|
|
|
@@ -184,10 +210,10 @@ class DeepSeekXReasoningEngine(Engine, DeepSeekMixin):
|
|
|
184
210
|
# Not Supported Features: Function Call、Json Output、FIM (Beta)
|
|
185
211
|
# Not Supported Parameters: temperature、top_p、presence_penalty、frequency_penalty、logprobs、top_logprobs
|
|
186
212
|
return {
|
|
187
|
-
"model": kwargs.get(
|
|
188
|
-
"seed": kwargs.get(
|
|
189
|
-
"max_tokens": kwargs.get(
|
|
190
|
-
"stop": kwargs.get(
|
|
191
|
-
"n": kwargs.get(
|
|
192
|
-
"logit_bias": kwargs.get(
|
|
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"),
|
|
193
219
|
}
|