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.
Files changed (127) hide show
  1. symai/__init__.py +198 -134
  2. symai/backend/base.py +51 -51
  3. symai/backend/engines/drawing/engine_bfl.py +33 -33
  4. symai/backend/engines/drawing/engine_gpt_image.py +4 -10
  5. symai/backend/engines/embedding/engine_llama_cpp.py +50 -35
  6. symai/backend/engines/embedding/engine_openai.py +22 -16
  7. symai/backend/engines/execute/engine_python.py +16 -16
  8. symai/backend/engines/files/engine_io.py +51 -49
  9. symai/backend/engines/imagecaptioning/engine_blip2.py +27 -23
  10. symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +53 -46
  11. symai/backend/engines/index/engine_pinecone.py +116 -88
  12. symai/backend/engines/index/engine_qdrant.py +1011 -0
  13. symai/backend/engines/index/engine_vectordb.py +78 -52
  14. symai/backend/engines/lean/engine_lean4.py +65 -25
  15. symai/backend/engines/neurosymbolic/__init__.py +28 -28
  16. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +137 -135
  17. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +145 -152
  18. symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
  19. symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +75 -49
  20. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +199 -155
  21. symai/backend/engines/neurosymbolic/engine_groq.py +106 -72
  22. symai/backend/engines/neurosymbolic/engine_huggingface.py +100 -67
  23. symai/backend/engines/neurosymbolic/engine_llama_cpp.py +121 -93
  24. symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +213 -132
  25. symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +180 -137
  26. symai/backend/engines/ocr/engine_apilayer.py +18 -20
  27. symai/backend/engines/output/engine_stdout.py +9 -9
  28. symai/backend/engines/{webscraping → scrape}/engine_requests.py +25 -11
  29. symai/backend/engines/search/engine_openai.py +95 -83
  30. symai/backend/engines/search/engine_parallel.py +665 -0
  31. symai/backend/engines/search/engine_perplexity.py +40 -41
  32. symai/backend/engines/search/engine_serpapi.py +33 -28
  33. symai/backend/engines/speech_to_text/engine_local_whisper.py +37 -27
  34. symai/backend/engines/symbolic/engine_wolframalpha.py +14 -8
  35. symai/backend/engines/text_to_speech/engine_openai.py +15 -19
  36. symai/backend/engines/text_vision/engine_clip.py +34 -28
  37. symai/backend/engines/userinput/engine_console.py +3 -4
  38. symai/backend/mixin/anthropic.py +48 -40
  39. symai/backend/mixin/deepseek.py +4 -5
  40. symai/backend/mixin/google.py +5 -4
  41. symai/backend/mixin/groq.py +2 -4
  42. symai/backend/mixin/openai.py +132 -110
  43. symai/backend/settings.py +14 -14
  44. symai/chat.py +164 -94
  45. symai/collect/dynamic.py +13 -11
  46. symai/collect/pipeline.py +39 -31
  47. symai/collect/stats.py +109 -69
  48. symai/components.py +556 -238
  49. symai/constraints.py +14 -5
  50. symai/core.py +1495 -1210
  51. symai/core_ext.py +55 -50
  52. symai/endpoints/api.py +113 -58
  53. symai/extended/api_builder.py +22 -17
  54. symai/extended/arxiv_pdf_parser.py +13 -5
  55. symai/extended/bibtex_parser.py +8 -4
  56. symai/extended/conversation.py +88 -69
  57. symai/extended/document.py +40 -27
  58. symai/extended/file_merger.py +45 -7
  59. symai/extended/graph.py +38 -24
  60. symai/extended/html_style_template.py +17 -11
  61. symai/extended/interfaces/blip_2.py +1 -1
  62. symai/extended/interfaces/clip.py +4 -2
  63. symai/extended/interfaces/console.py +5 -3
  64. symai/extended/interfaces/dall_e.py +3 -1
  65. symai/extended/interfaces/file.py +2 -0
  66. symai/extended/interfaces/flux.py +3 -1
  67. symai/extended/interfaces/gpt_image.py +15 -6
  68. symai/extended/interfaces/input.py +2 -1
  69. symai/extended/interfaces/llava.py +1 -1
  70. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
  71. symai/extended/interfaces/naive_vectordb.py +2 -2
  72. symai/extended/interfaces/ocr.py +4 -2
  73. symai/extended/interfaces/openai_search.py +2 -0
  74. symai/extended/interfaces/parallel.py +30 -0
  75. symai/extended/interfaces/perplexity.py +2 -0
  76. symai/extended/interfaces/pinecone.py +6 -4
  77. symai/extended/interfaces/python.py +2 -0
  78. symai/extended/interfaces/serpapi.py +2 -0
  79. symai/extended/interfaces/terminal.py +0 -1
  80. symai/extended/interfaces/tts.py +2 -1
  81. symai/extended/interfaces/whisper.py +2 -1
  82. symai/extended/interfaces/wolframalpha.py +1 -0
  83. symai/extended/metrics/__init__.py +1 -1
  84. symai/extended/metrics/similarity.py +5 -2
  85. symai/extended/os_command.py +31 -22
  86. symai/extended/packages/symdev.py +39 -34
  87. symai/extended/packages/sympkg.py +30 -27
  88. symai/extended/packages/symrun.py +46 -35
  89. symai/extended/repo_cloner.py +10 -9
  90. symai/extended/seo_query_optimizer.py +15 -12
  91. symai/extended/solver.py +104 -76
  92. symai/extended/summarizer.py +8 -7
  93. symai/extended/taypan_interpreter.py +10 -9
  94. symai/extended/vectordb.py +28 -15
  95. symai/formatter/formatter.py +39 -31
  96. symai/formatter/regex.py +46 -44
  97. symai/functional.py +184 -86
  98. symai/imports.py +85 -51
  99. symai/interfaces.py +1 -1
  100. symai/memory.py +33 -24
  101. symai/menu/screen.py +28 -19
  102. symai/misc/console.py +27 -27
  103. symai/misc/loader.py +4 -3
  104. symai/models/base.py +147 -76
  105. symai/models/errors.py +1 -1
  106. symai/ops/__init__.py +1 -1
  107. symai/ops/measures.py +17 -14
  108. symai/ops/primitives.py +933 -635
  109. symai/post_processors.py +28 -24
  110. symai/pre_processors.py +58 -52
  111. symai/processor.py +15 -9
  112. symai/prompts.py +714 -649
  113. symai/server/huggingface_server.py +115 -32
  114. symai/server/llama_cpp_server.py +14 -6
  115. symai/server/qdrant_server.py +206 -0
  116. symai/shell.py +98 -39
  117. symai/shellsv.py +307 -223
  118. symai/strategy.py +135 -81
  119. symai/symbol.py +276 -225
  120. symai/utils.py +62 -46
  121. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +19 -9
  122. symbolicai-1.1.0.dist-info/RECORD +168 -0
  123. symbolicai-1.0.0.dist-info/RECORD +0 -163
  124. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
  125. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
  126. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
  127. {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,7 @@ logging.getLogger("httpcore").setLevel(logging.ERROR)
21
21
  _NON_VERBOSE_OUTPUT = (
22
22
  "<META_INSTRUCTION/>\n"
23
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. "
24
+ '"Sure, let me...", "Hope that was helpful...", "Yes, I can help you with that...", etc. '
25
25
  "Consider well formatted output, e.g. for sentences use punctuation, spaces etc. or for code use "
26
26
  "indentation, etc. Never add meta instructions information to your output!\n\n"
27
27
  )
@@ -33,44 +33,56 @@ class GroqEngine(Engine):
33
33
  self.config = deepcopy(SYMAI_CONFIG)
34
34
  # In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
35
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
- return # do not initialize if not neurosymbolic; avoids conflict with llama.cpp check in EngineRepository.register_from_package
40
- openai.api_key = self.config['NEUROSYMBOLIC_ENGINE_API_KEY']
41
- self.model = self.config['NEUROSYMBOLIC_ENGINE_MODEL'] # Keep the original config name to avoid confusion in downstream tasks
36
+ self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] = api_key
37
+ self.config["NEUROSYMBOLIC_ENGINE_MODEL"] = model
38
+ if self.id() != "neurosymbolic":
39
+ return # do not initialize if not neurosymbolic; avoids conflict with llama.cpp check in EngineRepository.register_from_package
40
+ openai.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
41
+ self.model = self.config[
42
+ "NEUROSYMBOLIC_ENGINE_MODEL"
43
+ ] # Keep the original config name to avoid confusion in downstream tasks
42
44
  self.seed = None
43
45
  self.name = self.__class__.__name__
44
46
 
45
47
  try:
46
- self.client = openai.OpenAI(api_key=openai.api_key, base_url="https://api.groq.com/openai/v1")
48
+ self.client = openai.OpenAI(
49
+ api_key=openai.api_key, base_url="https://api.groq.com/openai/v1"
50
+ )
47
51
  except Exception as e:
48
- UserMessage(f'Failed to initialize OpenAI client. Please check your OpenAI library version. Caused by: {e}', raise_with=ValueError)
52
+ UserMessage(
53
+ f"Failed to initialize OpenAI client. Please check your OpenAI library version. Caused by: {e}",
54
+ raise_with=ValueError,
55
+ )
49
56
 
50
57
  def id(self) -> str:
51
- if self.config.get('NEUROSYMBOLIC_ENGINE_MODEL') and \
52
- self.config.get('NEUROSYMBOLIC_ENGINE_MODEL').startswith('groq'):
53
- return 'neurosymbolic'
54
- return super().id() # default to unregistered
58
+ if self.config.get("NEUROSYMBOLIC_ENGINE_MODEL") and self.config.get(
59
+ "NEUROSYMBOLIC_ENGINE_MODEL"
60
+ ).startswith("groq"):
61
+ return "neurosymbolic"
62
+ return super().id() # default to unregistered
55
63
 
56
64
  def command(self, *args, **kwargs):
57
65
  super().command(*args, **kwargs)
58
- if 'NEUROSYMBOLIC_ENGINE_API_KEY' in kwargs:
59
- openai.api_key = kwargs['NEUROSYMBOLIC_ENGINE_API_KEY']
60
- if 'NEUROSYMBOLIC_ENGINE_MODEL' in kwargs:
61
- self.model = kwargs['NEUROSYMBOLIC_ENGINE_MODEL']
62
- if 'seed' in kwargs:
63
- self.seed = kwargs['seed']
66
+ if "NEUROSYMBOLIC_ENGINE_API_KEY" in kwargs:
67
+ openai.api_key = kwargs["NEUROSYMBOLIC_ENGINE_API_KEY"]
68
+ if "NEUROSYMBOLIC_ENGINE_MODEL" in kwargs:
69
+ self.model = kwargs["NEUROSYMBOLIC_ENGINE_MODEL"]
70
+ if "seed" in kwargs:
71
+ self.seed = kwargs["seed"]
64
72
 
65
73
  def compute_required_tokens(self, _messages):
66
- UserMessage("Token counting not implemented for this engine.", raise_with=NotImplementedError)
74
+ UserMessage(
75
+ "Token counting not implemented for this engine.", raise_with=NotImplementedError
76
+ )
67
77
 
68
78
  def compute_remaining_tokens(self, _prompts: list) -> int:
69
- UserMessage("Token counting not implemented for this engine.", raise_with=NotImplementedError)
79
+ UserMessage(
80
+ "Token counting not implemented for this engine.", raise_with=NotImplementedError
81
+ )
70
82
 
71
83
  def _handle_prefix(self, model_name: str) -> str:
72
84
  """Handle prefix for model name."""
73
- return model_name.replace('groq:', '')
85
+ return model_name.replace("groq:", "")
74
86
 
75
87
  def _extract_thinking_content(self, output: list[str]) -> tuple[str | None, list[str]]:
76
88
  """Extract thinking content from model output if present and return cleaned output."""
@@ -81,7 +93,7 @@ class GroqEngine(Engine):
81
93
  if not content:
82
94
  return None, output
83
95
 
84
- think_pattern = r'<think>(.*?)</think>'
96
+ think_pattern = r"<think>(.*?)</think>"
85
97
  match = re.search(think_pattern, content, re.DOTALL)
86
98
 
87
99
  thinking_content = None
@@ -90,57 +102,67 @@ class GroqEngine(Engine):
90
102
  if not thinking_content:
91
103
  thinking_content = None
92
104
 
93
- cleaned_content = re.sub(think_pattern, '', content, flags=re.DOTALL).strip()
105
+ cleaned_content = re.sub(think_pattern, "", content, flags=re.DOTALL).strip()
94
106
  cleaned_output = [cleaned_content, *output[1:]]
95
107
 
96
108
  return thinking_content, cleaned_output
97
109
 
98
- # cumulative wait time is ~113s
99
- @retry(tries=8, delay=0.5, backoff=2, max_delay=30, jitter=(0, 0.5))
110
+ # cumulative wait time is < 30s
111
+ @retry(tries=8, delay=0.5, backoff=1.5, max_delay=5, jitter=(0, 0.5))
100
112
  def forward(self, argument):
101
113
  kwargs = argument.kwargs
102
114
  messages = argument.prop.prepared_input
103
115
  payload = self._prepare_request_payload(messages, argument)
104
- except_remedy = kwargs.get('except_remedy')
116
+ except_remedy = kwargs.get("except_remedy")
105
117
 
106
118
  try:
107
119
  res = self.client.chat.completions.create(**payload)
108
120
 
109
121
  except Exception as e:
110
- if openai.api_key is None or openai.api_key == '':
111
- msg = 'Groq API key is not set. Please set it in the config file or pass it as an argument to the command method.'
122
+ if openai.api_key is None or openai.api_key == "":
123
+ msg = "Groq API key is not set. Please set it in the config file or pass it as an argument to the command method."
112
124
  UserMessage(msg)
113
- if self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] is None or self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] == '':
125
+ if (
126
+ self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] is None
127
+ or self.config["NEUROSYMBOLIC_ENGINE_API_KEY"] == ""
128
+ ):
114
129
  UserMessage(msg, raise_with=ValueError)
115
- openai.api_key = self.config['NEUROSYMBOLIC_ENGINE_API_KEY']
130
+ openai.api_key = self.config["NEUROSYMBOLIC_ENGINE_API_KEY"]
116
131
 
117
132
  callback = self.client.chat.completions.create
118
- kwargs['model'] = self._handle_prefix(kwargs['model']) if 'model' in kwargs else self._handle_prefix(self.model)
133
+ kwargs["model"] = (
134
+ self._handle_prefix(kwargs["model"])
135
+ if "model" in kwargs
136
+ else self._handle_prefix(self.model)
137
+ )
119
138
 
120
139
  if except_remedy is not None:
121
140
  res = except_remedy(self, e, callback, argument)
122
141
  else:
123
- UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
142
+ UserMessage(f"Error during generation. Caused by: {e}", raise_with=ValueError)
124
143
 
125
- metadata = {'raw_output': res}
126
- if payload.get('tools'):
144
+ metadata = {"raw_output": res}
145
+ if payload.get("tools"):
127
146
  metadata = self._process_function_calls(res, metadata)
128
147
 
129
148
  output = [r.message.content for r in res.choices]
130
149
  thinking, output = self._extract_thinking_content(output)
131
150
  if thinking:
132
- metadata['thinking'] = thinking
151
+ metadata["thinking"] = thinking
133
152
 
134
153
  return output, metadata
135
154
 
136
155
  def _prepare_raw_input(self, argument):
137
156
  if not argument.prop.processed_input:
138
- UserMessage('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
157
+ UserMessage(
158
+ "Need to provide a prompt instruction to the engine if raw_input is enabled.",
159
+ raise_with=ValueError,
160
+ )
139
161
  value = argument.prop.processed_input
140
162
  # convert to dict if not already
141
163
  if not isinstance(value, list):
142
164
  if not isinstance(value, dict):
143
- value = {'role': 'user', 'content': str(value)}
165
+ value = {"role": "user", "content": str(value)}
144
166
  value = [value]
145
167
  return value
146
168
 
@@ -156,14 +178,14 @@ class GroqEngine(Engine):
156
178
  system, user_prompt = self._apply_self_prompt_if_needed(argument, system, user_prompt)
157
179
 
158
180
  argument.prop.prepared_input = [
159
- { "role": "system", "content": system },
181
+ {"role": "system", "content": system},
160
182
  user_prompt,
161
183
  ]
162
184
 
163
185
  def _validate_response_format(self, argument) -> None:
164
186
  if argument.prop.response_format:
165
187
  response_format = argument.prop.response_format
166
- assert response_format.get('type') is not None, (
188
+ assert response_format.get("type") is not None, (
167
189
  'Expected format `{ "type": "json_object" }`! We are using the OpenAI compatible API for Groq. '
168
190
  "See more here: https://console.groq.com/docs/tool-use"
169
191
  )
@@ -206,36 +228,38 @@ class GroqEngine(Engine):
206
228
  return str(argument.prop.processed_input)
207
229
 
208
230
  def _apply_self_prompt_if_needed(self, argument, system, user_prompt):
209
- if argument.prop.instance._kwargs.get('self_prompt', False) or argument.prop.self_prompt:
231
+ if argument.prop.instance._kwargs.get("self_prompt", False) or argument.prop.self_prompt:
210
232
  self_prompter = SelfPrompt()
211
- res = self_prompter({'user': user_prompt['content'], 'system': system})
233
+ res = self_prompter({"user": user_prompt["content"], "system": system})
212
234
  if res is None:
213
235
  UserMessage("Self-prompting failed!", raise_with=ValueError)
214
- return res['system'], {"role": "user", "content": res['user']}
236
+ return res["system"], {"role": "user", "content": res["user"]}
215
237
  return system, user_prompt
216
238
 
217
239
  def _process_function_calls(self, res, metadata):
218
240
  hit = False
219
241
  if (
220
- hasattr(res, 'choices')
242
+ hasattr(res, "choices")
221
243
  and res.choices
222
- and hasattr(res.choices[0], 'message')
244
+ and hasattr(res.choices[0], "message")
223
245
  and res.choices[0].message
224
- and hasattr(res.choices[0].message, 'tool_calls')
246
+ and hasattr(res.choices[0].message, "tool_calls")
225
247
  and res.choices[0].message.tool_calls
226
248
  ):
227
249
  for tool_call in res.choices[0].message.tool_calls:
228
- if hasattr(tool_call, 'function') and tool_call.function:
250
+ if hasattr(tool_call, "function") and tool_call.function:
229
251
  if hit:
230
- UserMessage("Multiple function calls detected in the response but only the first one will be processed.")
252
+ UserMessage(
253
+ "Multiple function calls detected in the response but only the first one will be processed."
254
+ )
231
255
  break
232
256
  try:
233
257
  args_dict = json.loads(tool_call.function.arguments)
234
258
  except json.JSONDecodeError:
235
259
  args_dict = {}
236
- metadata['function_call'] = {
237
- 'name': tool_call.function.name,
238
- 'arguments': args_dict
260
+ metadata["function_call"] = {
261
+ "name": tool_call.function.name,
262
+ "arguments": args_dict,
239
263
  }
240
264
  hit = True
241
265
  return metadata
@@ -253,43 +277,53 @@ class GroqEngine(Engine):
253
277
  "stream_options",
254
278
  ]:
255
279
  if param in kwargs:
256
- UserMessage(f"The parameter {param} is not supported by the Groq API. It will be ignored.")
280
+ UserMessage(
281
+ f"The parameter {param} is not supported by the Groq API. It will be ignored."
282
+ )
257
283
  del kwargs[param]
258
284
 
259
- n = kwargs.get('n', 1)
285
+ n = kwargs.get("n", 1)
260
286
  if n > 1:
261
- UserMessage("If N is supplied, it must be equal to 1. We default to 1 to not crash your program.")
287
+ UserMessage(
288
+ "If N is supplied, it must be equal to 1. We default to 1 to not crash your program."
289
+ )
262
290
  n = 1
263
291
 
264
292
  # Handle Groq JSON-mode quirk: JSON Object Mode internally uses a constrainer tool.
265
- response_format = kwargs.get('response_format')
266
- tool_choice = kwargs.get('tool_choice', 'auto' if kwargs.get('tools') else 'none')
267
- tools = kwargs.get('tools')
268
- if response_format and isinstance(response_format, dict) and response_format.get('type') == 'json_object':
269
- if tool_choice in (None, 'none'):
270
- tool_choice = 'auto'
293
+ response_format = kwargs.get("response_format")
294
+ tool_choice = kwargs.get("tool_choice", "auto" if kwargs.get("tools") else "none")
295
+ tools = kwargs.get("tools")
296
+ if (
297
+ response_format
298
+ and isinstance(response_format, dict)
299
+ and response_format.get("type") == "json_object"
300
+ ):
301
+ if tool_choice in (None, "none"):
302
+ tool_choice = "auto"
271
303
  if tools:
272
304
  tools = None
273
305
 
274
306
  payload = {
275
307
  "messages": messages,
276
- "model": self._handle_prefix(kwargs.get('model', self.model)),
277
- "seed": kwargs.get('seed', self.seed),
278
- "max_completion_tokens": kwargs.get('max_completion_tokens'),
279
- "stop": kwargs.get('stop'),
280
- "temperature": kwargs.get('temperature', 1), # Default temperature for gpt-oss-120b
281
- "frequency_penalty": kwargs.get('frequency_penalty', 0),
282
- "presence_penalty": kwargs.get('presence_penalty', 0),
283
- "reasoning_effort": kwargs.get('reasoning_effort'),
284
- "service_tier": kwargs.get('service_tier', 'on_demand'),
285
- "top_p": kwargs.get('top_p', 1),
308
+ "model": self._handle_prefix(kwargs.get("model", self.model)),
309
+ "seed": kwargs.get("seed", self.seed),
310
+ "max_completion_tokens": kwargs.get("max_completion_tokens"),
311
+ "stop": kwargs.get("stop"),
312
+ "temperature": kwargs.get("temperature", 1), # Default temperature for gpt-oss-120b
313
+ "frequency_penalty": kwargs.get("frequency_penalty", 0),
314
+ "presence_penalty": kwargs.get("presence_penalty", 0),
315
+ "reasoning_effort": kwargs.get("reasoning_effort"),
316
+ "service_tier": kwargs.get("service_tier", "on_demand"),
317
+ "top_p": kwargs.get("top_p", 1),
286
318
  "n": n,
287
319
  "tools": tools,
288
320
  "tool_choice": tool_choice,
289
321
  "response_format": response_format,
290
322
  }
291
323
 
292
- if not self._handle_prefix(self.model).startswith('qwen') or not self._handle_prefix(self.model).startswith('openai'):
293
- del payload['reasoning_effort']
324
+ if not self._handle_prefix(self.model).startswith("qwen") or not self._handle_prefix(
325
+ self.model
326
+ ).startswith("openai"):
327
+ del payload["reasoning_effort"]
294
328
 
295
329
  return payload
@@ -18,31 +18,41 @@ class HFTokenizer:
18
18
 
19
19
  @staticmethod
20
20
  def encode(text: str, add_special_tokens: bool = False) -> list[int]:
21
- res = requests.post(f"{HFTokenizer._server_endpoint}/tokenize", json={
22
- "input": text,
23
- "add_special_tokens": add_special_tokens,
24
- })
21
+ res = requests.post(
22
+ f"{HFTokenizer._server_endpoint}/tokenize",
23
+ json={
24
+ "input": text,
25
+ "add_special_tokens": add_special_tokens,
26
+ },
27
+ )
25
28
 
26
29
  if res.status_code != 200:
27
- UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
30
+ UserMessage(
31
+ f"Request failed with status code: {res.status_code}", raise_with=ValueError
32
+ )
28
33
 
29
34
  res = res.json()
30
35
 
31
- return res['tokens']
36
+ return res["tokens"]
32
37
 
33
38
  @staticmethod
34
39
  def decode(tokens: list[int], skip_special_tokens: bool = True) -> str:
35
- res = requests.post(f"{HFTokenizer._server_endpoint}/detokenize", json={
36
- "tokens": tokens,
37
- "skip_special_tokens": skip_special_tokens,
38
- })
40
+ res = requests.post(
41
+ f"{HFTokenizer._server_endpoint}/detokenize",
42
+ json={
43
+ "tokens": tokens,
44
+ "skip_special_tokens": skip_special_tokens,
45
+ },
46
+ )
39
47
 
40
48
  if res.status_code != 200:
41
- UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
49
+ UserMessage(
50
+ f"Request failed with status code: {res.status_code}", raise_with=ValueError
51
+ )
42
52
 
43
53
  res = res.json()
44
54
 
45
- return res['text']
55
+ return res["text"]
46
56
 
47
57
 
48
58
  class HFEngine(Engine):
@@ -51,70 +61,90 @@ class HFEngine(Engine):
51
61
  self.config = deepcopy(SYMAI_CONFIG)
52
62
  # In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
53
63
  if model is not None:
54
- self.config['NEUROSYMBOLIC_ENGINE_MODEL'] = model
55
- if self.id() != 'neurosymbolic':
64
+ self.config["NEUROSYMBOLIC_ENGINE_MODEL"] = model
65
+ if self.id() != "neurosymbolic":
56
66
  return
57
- if not SYMSERVER_CONFIG.get('online'):
58
- UserMessage('You are using the huggingface engine, but the server endpoint is not started. Please start the server with `symserver [--args]` or run `symserver --help` to see the available options for this engine.', raise_with=ValueError)
59
- self.server_endpoint = f"http://{SYMSERVER_CONFIG.get('host')}:{SYMSERVER_CONFIG.get('port')}"
60
- self.tokenizer = HFTokenizer # backwards compatibility with how we handle tokenization, i.e. self.tokenizer().encode(...)
67
+ if not SYMSERVER_CONFIG.get("online"):
68
+ UserMessage(
69
+ "You are using the huggingface engine, but the server endpoint is not started. Please start the server with `symserver [--args]` or run `symserver --help` to see the available options for this engine.",
70
+ raise_with=ValueError,
71
+ )
72
+ self.server_endpoint = (
73
+ f"http://{SYMSERVER_CONFIG.get('host')}:{SYMSERVER_CONFIG.get('port')}"
74
+ )
75
+ self.tokenizer = HFTokenizer # backwards compatibility with how we handle tokenization, i.e. self.tokenizer().encode(...)
61
76
  self.name = self.__class__.__name__
62
77
 
63
78
  def id(self) -> str:
64
- if self.config.get('NEUROSYMBOLIC_ENGINE_MODEL') and self.config.get('NEUROSYMBOLIC_ENGINE_MODEL').startswith('huggingface'):
65
- return 'neurosymbolic'
66
- return super().id() # default to unregistered
79
+ if self.config.get("NEUROSYMBOLIC_ENGINE_MODEL") and self.config.get(
80
+ "NEUROSYMBOLIC_ENGINE_MODEL"
81
+ ).startswith("huggingface"):
82
+ return "neurosymbolic"
83
+ return super().id() # default to unregistered
67
84
 
68
85
  def command(self, *args, **kwargs):
69
86
  super().command(*args, **kwargs)
70
- if 'NEUROSYMBOLIC_ENGINE_MODEL' in kwargs:
71
- self.model = kwargs['NEUROSYMBOLIC_ENGINE_MODEL']
72
- if 'seed' in kwargs:
73
- self.seed = kwargs['seed']
74
- if 'except_remedy' in kwargs:
75
- self.except_remedy = kwargs['except_remedy']
87
+ if "NEUROSYMBOLIC_ENGINE_MODEL" in kwargs:
88
+ self.model = kwargs["NEUROSYMBOLIC_ENGINE_MODEL"]
89
+ if "seed" in kwargs:
90
+ self.seed = kwargs["seed"]
91
+ if "except_remedy" in kwargs:
92
+ self.except_remedy = kwargs["except_remedy"]
76
93
 
77
94
  def compute_required_tokens(self, _messages) -> int:
78
- UserMessage('Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.', raise_with=NotImplementedError)
95
+ UserMessage(
96
+ "Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.",
97
+ raise_with=NotImplementedError,
98
+ )
99
+
79
100
  def compute_remaining_tokens(self, _prompts: list) -> int:
80
- UserMessage('Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.', raise_with=NotImplementedError)
101
+ UserMessage(
102
+ "Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.",
103
+ raise_with=NotImplementedError,
104
+ )
105
+
81
106
  def forward(self, argument):
82
- kwargs = argument.kwargs
107
+ kwargs = argument.kwargs
83
108
  prompts = argument.prop.prepared_input
84
109
 
85
- stop = kwargs.get('stop')
86
- seed = kwargs.get('seed')
87
- temperature = kwargs.get('temperature', 1.)
88
- top_p = kwargs.get('top_p', 1.)
89
- top_k = kwargs.get('top_k', 50)
90
- max_tokens = kwargs.get('max_tokens', 2048)
91
- max_tokens_forcing = kwargs.get('max_tokens_forcing')
92
- logprobs = kwargs.get('logprobs', True)
93
- do_sample = kwargs.get('do_sample', True)
94
- num_beams = kwargs.get('num_beams', 1)
95
- num_beam_groups = kwargs.get('num_beam_groups', 1)
96
- eos_token_id = kwargs.get('eos_token_id')
97
- except_remedy = kwargs.get('except_remedy')
110
+ stop = kwargs.get("stop")
111
+ seed = kwargs.get("seed")
112
+ temperature = kwargs.get("temperature", 1.0)
113
+ top_p = kwargs.get("top_p", 1.0)
114
+ top_k = kwargs.get("top_k", 50)
115
+ max_tokens = kwargs.get("max_tokens", 2048)
116
+ max_tokens_forcing = kwargs.get("max_tokens_forcing")
117
+ logprobs = kwargs.get("logprobs", True)
118
+ do_sample = kwargs.get("do_sample", True)
119
+ num_beams = kwargs.get("num_beams", 1)
120
+ num_beam_groups = kwargs.get("num_beam_groups", 1)
121
+ eos_token_id = kwargs.get("eos_token_id")
122
+ except_remedy = kwargs.get("except_remedy")
98
123
 
99
124
  try:
100
- res = requests.post(f"{self.server_endpoint}/chat", json={
101
- "messages": prompts,
102
- "temperature": temperature,
103
- "top_p": top_p,
104
- "stop": stop,
105
- "seed": seed,
106
- "max_tokens": max_tokens,
107
- "max_tokens_forcing": max_tokens_forcing,
108
- "top_k": top_k,
109
- "logprobs": logprobs,
110
- "do_sample": do_sample,
111
- "num_beams": num_beams,
112
- "num_beam_groups": num_beam_groups,
113
- "eos_token_id": eos_token_id,
114
- })
125
+ res = requests.post(
126
+ f"{self.server_endpoint}/chat",
127
+ json={
128
+ "messages": prompts,
129
+ "temperature": temperature,
130
+ "top_p": top_p,
131
+ "stop": stop,
132
+ "seed": seed,
133
+ "max_tokens": max_tokens,
134
+ "max_tokens_forcing": max_tokens_forcing,
135
+ "top_k": top_k,
136
+ "logprobs": logprobs,
137
+ "do_sample": do_sample,
138
+ "num_beams": num_beams,
139
+ "num_beam_groups": num_beam_groups,
140
+ "eos_token_id": eos_token_id,
141
+ },
142
+ )
115
143
 
116
144
  if res.status_code != 200:
117
- UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
145
+ UserMessage(
146
+ f"Request failed with status code: {res.status_code}", raise_with=ValueError
147
+ )
118
148
 
119
149
  res = res.json()
120
150
 
@@ -122,21 +152,24 @@ class HFEngine(Engine):
122
152
  if except_remedy is not None:
123
153
  res = except_remedy(self, e, argument)
124
154
  else:
125
- UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
155
+ UserMessage(f"Error during generation. Caused by: {e}", raise_with=ValueError)
126
156
 
127
- metadata = {'raw_output': res}
157
+ metadata = {"raw_output": res}
128
158
 
129
- rsp = [r['message']['content'] for r in res['choices']]
159
+ rsp = [r["message"]["content"] for r in res["choices"]]
130
160
  output = rsp if isinstance(prompts, list) else rsp[0]
131
161
  return output, metadata
132
162
 
133
163
  def _prepare_raw_input(self, argument):
134
164
  if not argument.prop.processed_input:
135
- UserMessage('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
165
+ UserMessage(
166
+ "Need to provide a prompt instruction to the engine if raw_input is enabled.",
167
+ raise_with=ValueError,
168
+ )
136
169
  value = argument.prop.processed_input
137
170
  if not isinstance(value, list):
138
171
  if not isinstance(value, dict):
139
- value = {'role': 'user', 'content': str(value)}
172
+ value = {"role": "user", "content": str(value)}
140
173
  value = [value]
141
174
  return value
142
175
 
@@ -147,12 +180,12 @@ class HFEngine(Engine):
147
180
 
148
181
  _non_verbose_output = """<META_INSTRUCTION/>\n You will NOT output verbose preambles or post explanation, such as "Sure, let me...", "Hope that was helpful...", "Yes, I can help you with that...", etc. You will consider well formatted output, e.g. for sentences you will use punctuation, spaces, etc. or for code indentation, etc.\n"""
149
182
 
150
- #@TODO: Non-trivial how to handle user/system/assistant roles;
183
+ # @TODO: Non-trivial how to handle user/system/assistant roles;
151
184
  user = ""
152
185
 
153
186
  if argument.prop.suppress_verbose_output:
154
187
  user += _non_verbose_output
155
- user = f'{user}\n' if user and len(user) > 0 else ''
188
+ user = f"{user}\n" if user and len(user) > 0 else ""
156
189
 
157
190
  ref = argument.prop.instance
158
191
  static_ctxt, dyn_ctxt = ref.global_context
@@ -179,5 +212,5 @@ class HFEngine(Engine):
179
212
  user += str(argument.prop.processed_input)
180
213
 
181
214
  argument.prop.prepared_input = [
182
- { "role": "user", "content": user },
215
+ {"role": "user", "content": user},
183
216
  ]