symbolicai 0.20.2__py3-none-any.whl → 1.0.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 (123) hide show
  1. symai/__init__.py +96 -64
  2. symai/backend/base.py +93 -80
  3. symai/backend/engines/drawing/engine_bfl.py +12 -11
  4. symai/backend/engines/drawing/engine_gpt_image.py +108 -87
  5. symai/backend/engines/embedding/engine_llama_cpp.py +25 -28
  6. symai/backend/engines/embedding/engine_openai.py +3 -5
  7. symai/backend/engines/execute/engine_python.py +6 -5
  8. symai/backend/engines/files/engine_io.py +74 -67
  9. symai/backend/engines/imagecaptioning/engine_blip2.py +3 -3
  10. symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +54 -38
  11. symai/backend/engines/index/engine_pinecone.py +23 -24
  12. symai/backend/engines/index/engine_vectordb.py +16 -14
  13. symai/backend/engines/lean/engine_lean4.py +38 -34
  14. symai/backend/engines/neurosymbolic/__init__.py +41 -13
  15. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +262 -182
  16. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +263 -191
  17. symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +53 -49
  18. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +212 -211
  19. symai/backend/engines/neurosymbolic/engine_groq.py +87 -63
  20. symai/backend/engines/neurosymbolic/engine_huggingface.py +21 -24
  21. symai/backend/engines/neurosymbolic/engine_llama_cpp.py +117 -48
  22. symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +256 -229
  23. symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +270 -150
  24. symai/backend/engines/ocr/engine_apilayer.py +6 -8
  25. symai/backend/engines/output/engine_stdout.py +1 -4
  26. symai/backend/engines/search/engine_openai.py +7 -7
  27. symai/backend/engines/search/engine_perplexity.py +5 -5
  28. symai/backend/engines/search/engine_serpapi.py +12 -14
  29. symai/backend/engines/speech_to_text/engine_local_whisper.py +20 -27
  30. symai/backend/engines/symbolic/engine_wolframalpha.py +3 -3
  31. symai/backend/engines/text_to_speech/engine_openai.py +5 -7
  32. symai/backend/engines/text_vision/engine_clip.py +7 -11
  33. symai/backend/engines/userinput/engine_console.py +3 -3
  34. symai/backend/engines/webscraping/engine_requests.py +81 -48
  35. symai/backend/mixin/__init__.py +13 -0
  36. symai/backend/mixin/anthropic.py +4 -2
  37. symai/backend/mixin/deepseek.py +2 -0
  38. symai/backend/mixin/google.py +2 -0
  39. symai/backend/mixin/openai.py +11 -3
  40. symai/backend/settings.py +83 -16
  41. symai/chat.py +101 -78
  42. symai/collect/__init__.py +7 -1
  43. symai/collect/dynamic.py +77 -69
  44. symai/collect/pipeline.py +35 -27
  45. symai/collect/stats.py +75 -63
  46. symai/components.py +198 -169
  47. symai/constraints.py +15 -12
  48. symai/core.py +698 -359
  49. symai/core_ext.py +32 -34
  50. symai/endpoints/api.py +80 -73
  51. symai/extended/.DS_Store +0 -0
  52. symai/extended/__init__.py +46 -12
  53. symai/extended/api_builder.py +11 -8
  54. symai/extended/arxiv_pdf_parser.py +13 -12
  55. symai/extended/bibtex_parser.py +2 -3
  56. symai/extended/conversation.py +101 -90
  57. symai/extended/document.py +17 -10
  58. symai/extended/file_merger.py +18 -13
  59. symai/extended/graph.py +18 -13
  60. symai/extended/html_style_template.py +2 -4
  61. symai/extended/interfaces/blip_2.py +1 -2
  62. symai/extended/interfaces/clip.py +1 -2
  63. symai/extended/interfaces/console.py +7 -1
  64. symai/extended/interfaces/dall_e.py +1 -1
  65. symai/extended/interfaces/flux.py +1 -1
  66. symai/extended/interfaces/gpt_image.py +1 -1
  67. symai/extended/interfaces/input.py +1 -1
  68. symai/extended/interfaces/llava.py +0 -1
  69. symai/extended/interfaces/naive_vectordb.py +7 -8
  70. symai/extended/interfaces/naive_webscraping.py +1 -1
  71. symai/extended/interfaces/ocr.py +1 -1
  72. symai/extended/interfaces/pinecone.py +6 -5
  73. symai/extended/interfaces/serpapi.py +1 -1
  74. symai/extended/interfaces/terminal.py +2 -3
  75. symai/extended/interfaces/tts.py +1 -1
  76. symai/extended/interfaces/whisper.py +1 -1
  77. symai/extended/interfaces/wolframalpha.py +1 -1
  78. symai/extended/metrics/__init__.py +11 -1
  79. symai/extended/metrics/similarity.py +11 -13
  80. symai/extended/os_command.py +17 -16
  81. symai/extended/packages/__init__.py +29 -3
  82. symai/extended/packages/symdev.py +19 -16
  83. symai/extended/packages/sympkg.py +12 -9
  84. symai/extended/packages/symrun.py +21 -19
  85. symai/extended/repo_cloner.py +11 -10
  86. symai/extended/seo_query_optimizer.py +1 -2
  87. symai/extended/solver.py +20 -23
  88. symai/extended/summarizer.py +4 -3
  89. symai/extended/taypan_interpreter.py +10 -12
  90. symai/extended/vectordb.py +99 -82
  91. symai/formatter/__init__.py +9 -1
  92. symai/formatter/formatter.py +12 -16
  93. symai/formatter/regex.py +62 -63
  94. symai/functional.py +176 -122
  95. symai/imports.py +136 -127
  96. symai/interfaces.py +56 -27
  97. symai/memory.py +14 -13
  98. symai/misc/console.py +49 -39
  99. symai/misc/loader.py +5 -3
  100. symai/models/__init__.py +17 -1
  101. symai/models/base.py +269 -181
  102. symai/models/errors.py +0 -1
  103. symai/ops/__init__.py +32 -22
  104. symai/ops/measures.py +11 -15
  105. symai/ops/primitives.py +348 -228
  106. symai/post_processors.py +32 -28
  107. symai/pre_processors.py +39 -41
  108. symai/processor.py +6 -4
  109. symai/prompts.py +59 -45
  110. symai/server/huggingface_server.py +23 -20
  111. symai/server/llama_cpp_server.py +7 -5
  112. symai/shell.py +3 -4
  113. symai/shellsv.py +499 -375
  114. symai/strategy.py +517 -287
  115. symai/symbol.py +111 -116
  116. symai/utils.py +42 -36
  117. {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/METADATA +4 -2
  118. symbolicai-1.0.0.dist-info/RECORD +163 -0
  119. symbolicai-0.20.2.dist-info/RECORD +0 -162
  120. {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/WHEEL +0 -0
  121. {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/entry_points.txt +0 -0
  122. {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/licenses/LICENSE +0 -0
  123. {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/top_level.txt +0 -0
@@ -2,15 +2,14 @@ import asyncio
2
2
  import json
3
3
  import logging
4
4
  from copy import deepcopy
5
+ from typing import Any, ClassVar
5
6
 
6
7
  import aiohttp
7
- import httpx
8
8
  import nest_asyncio
9
- import requests
10
9
 
11
10
  from ....core import Argument
12
11
  from ....core_ext import retry
13
- from ....utils import CustomUserWarning
12
+ from ....utils import UserMessage
14
13
  from ...base import Engine
15
14
  from ...settings import SYMAI_CONFIG, SYMSERVER_CONFIG
16
15
 
@@ -25,14 +24,14 @@ class LlamaCppTokenizer:
25
24
 
26
25
  @staticmethod
27
26
  async def _encode(text: str) -> list[int]:
28
- async with aiohttp.ClientSession() as session:
29
- async with session.post(f"{LlamaCppTokenizer._server_endpoint}/extras/tokenize", json={
30
- "input": text,
31
- }) as res:
32
- if res.status != 200:
33
- CustomUserWarning(f"Request failed with status code: {res.status}", raise_with=ValueError)
34
- res = await res.json()
35
- return res['tokens']
27
+ async with aiohttp.ClientSession() as session, session.post(
28
+ f"{LlamaCppTokenizer._server_endpoint}/tokenize",
29
+ json={"content": text},
30
+ ) as res:
31
+ if res.status != 200:
32
+ UserMessage(f"Request failed with status code: {res.status}", raise_with=ValueError)
33
+ response_json = await res.json()
34
+ return response_json['tokens']
36
35
 
37
36
  @staticmethod
38
37
  def encode(text: str) -> list[int]:
@@ -45,14 +44,14 @@ class LlamaCppTokenizer:
45
44
 
46
45
  @staticmethod
47
46
  async def _decode(tokens: list[int]) -> str:
48
- async with aiohttp.ClientSession() as session:
49
- async with session.post(f"{LlamaCppTokenizer._server_endpoint}/extras/detokenize", json={
50
- "tokens": tokens,
51
- }) as res:
52
- if res.status != 200:
53
- CustomUserWarning(f"Request failed with status code: {res.status}", raise_with=ValueError)
54
- res = await res.json()
55
- return res['text']
47
+ async with aiohttp.ClientSession() as session, session.post(
48
+ f"{LlamaCppTokenizer._server_endpoint}/detokenize",
49
+ json={"tokens": tokens},
50
+ ) as res:
51
+ if res.status != 200:
52
+ UserMessage(f"Request failed with status code: {res.status}", raise_with=ValueError)
53
+ response_json = await res.json()
54
+ return response_json['content']
56
55
 
57
56
  @staticmethod
58
57
  def decode(tokens: list[int]) -> str:
@@ -65,7 +64,7 @@ class LlamaCppTokenizer:
65
64
 
66
65
 
67
66
  class LlamaCppEngine(Engine):
68
- _retry_params = {
67
+ _retry_params: ClassVar[dict[str, Any]] = {
69
68
  'tries': 5,
70
69
  'delay': 2,
71
70
  'max_delay': 60,
@@ -73,7 +72,7 @@ class LlamaCppEngine(Engine):
73
72
  'jitter': (1, 5),
74
73
  'graceful': True
75
74
  }
76
- _timeout_params = {
75
+ _timeout_params: ClassVar[dict[str, Any]] = {
77
76
  'read': None,
78
77
  'connect': None,
79
78
  }
@@ -91,7 +90,7 @@ class LlamaCppEngine(Engine):
91
90
  if self.id() != 'neurosymbolic':
92
91
  return
93
92
  if not SYMSERVER_CONFIG.get('online'):
94
- CustomUserWarning('You are using the llama.cpp 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)
93
+ UserMessage('You are using the llama.cpp 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)
95
94
  self.server_endpoint = f"http://{SYMSERVER_CONFIG.get('--host')}:{SYMSERVER_CONFIG.get('--port')}"
96
95
  self.tokenizer = LlamaCppTokenizer # backwards compatibility with how we handle tokenization, i.e. self.tokenizer().encode(...)
97
96
  self.timeout_params = self._validate_timeout_params(timeout_params)
@@ -112,23 +111,23 @@ class LlamaCppEngine(Engine):
112
111
  if 'except_remedy' in kwargs:
113
112
  self.except_remedy = kwargs['except_remedy']
114
113
 
115
- def compute_required_tokens(self, messages) -> int:
114
+ def compute_required_tokens(self, _messages) -> int:
116
115
  #@TODO: quite non-trivial how to handle this with the llama.cpp server
117
- CustomUserWarning('Not implemented for llama.cpp!', raise_with=NotImplementedError)
116
+ UserMessage('Not implemented for llama.cpp!', raise_with=NotImplementedError)
118
117
 
119
- def compute_remaining_tokens(self, prompts: list) -> int:
118
+ def compute_remaining_tokens(self, _prompts: list) -> int:
120
119
  #@TODO: quite non-trivial how to handle this with the llama.cpp server
121
- CustomUserWarning('Not implemented for llama.cpp!', raise_with=NotImplementedError)
120
+ UserMessage('Not implemented for llama.cpp!', raise_with=NotImplementedError)
122
121
 
123
122
  def _validate_timeout_params(self, timeout_params):
124
123
  if not isinstance(timeout_params, dict):
125
- CustomUserWarning("timeout_params must be a dictionary", raise_with=ValueError)
124
+ UserMessage("timeout_params must be a dictionary", raise_with=ValueError)
126
125
  assert all(key in timeout_params for key in ['read', 'connect']), "Available keys: ['read', 'connect']"
127
126
  return timeout_params
128
127
 
129
128
  def _validate_retry_params(self, retry_params):
130
129
  if not isinstance(retry_params, dict):
131
- CustomUserWarning("retry_params must be a dictionary", raise_with=ValueError)
130
+ UserMessage("retry_params must be a dictionary", raise_with=ValueError)
132
131
  assert all(key in retry_params for key in ['tries', 'delay', 'max_delay', 'backoff', 'jitter', 'graceful']), "Available keys: ['tries', 'delay', 'max_delay', 'backoff', 'jitter', 'graceful']"
133
132
  return retry_params
134
133
 
@@ -138,7 +137,7 @@ class LlamaCppEngine(Engine):
138
137
  try:
139
138
  current_loop = asyncio.get_event_loop()
140
139
  if current_loop.is_closed():
141
- CustomUserWarning("Event loop is closed.", raise_with=RuntimeError)
140
+ UserMessage("Event loop is closed.", raise_with=RuntimeError)
142
141
  return current_loop
143
142
  except RuntimeError:
144
143
  new_loop = asyncio.new_event_loop()
@@ -148,7 +147,7 @@ class LlamaCppEngine(Engine):
148
147
  def _prepare_request_payload(self, argument: Argument) -> dict:
149
148
  """Prepares the request payload from the argument."""
150
149
  kwargs = argument.kwargs
151
- return {
150
+ payload = {
152
151
  "messages": argument.prop.prepared_input,
153
152
  "temperature": kwargs.get('temperature', 0.6),
154
153
  "frequency_penalty": kwargs.get('frequency_penalty', 0),
@@ -162,12 +161,28 @@ class LlamaCppEngine(Engine):
162
161
  "repeat_penalty": kwargs.get('repeat_penalty', 1),
163
162
  "logits_bias": kwargs.get('logits_bias'),
164
163
  "logprobs": kwargs.get('logprobs', False),
165
- "functions": kwargs.get('functions'),
166
- "function_call": kwargs.get('function_call'),
167
164
  "grammar": kwargs.get('grammar'),
168
165
  "response_format": kwargs.get('response_format'),
169
166
  }
170
167
 
168
+ model = SYMSERVER_CONFIG.get('-m') or SYMSERVER_CONFIG.get('--model')
169
+ if model:
170
+ payload["model"] = model
171
+
172
+ tools = kwargs.get('tools')
173
+ if tools:
174
+ payload["tools"] = tools
175
+
176
+ tool_choice = kwargs.get('tool_choice')
177
+ if tool_choice is not None:
178
+ payload["tool_choice"] = tool_choice
179
+
180
+ extra_body = kwargs.get('extra_body')
181
+ if isinstance(extra_body, dict):
182
+ payload.update(extra_body)
183
+
184
+ return payload
185
+
171
186
  async def _arequest(self, payload: dict) -> dict:
172
187
  """Makes an async HTTP request to the llama.cpp server."""
173
188
  @retry(**self.retry_params)
@@ -176,17 +191,29 @@ class LlamaCppEngine(Engine):
176
191
  sock_connect=self.timeout_params['connect'],
177
192
  sock_read=self.timeout_params['read']
178
193
  )
179
- async with aiohttp.ClientSession(timeout=timeout) as session:
180
- async with session.post(
181
- f"{self.server_endpoint}/v1/chat/completions",
182
- json=payload
183
- ) as res:
184
- if res.status != 200:
185
- CustomUserWarning(f"Request failed with status code: {res.status}", raise_with=ValueError)
186
- return await res.json()
194
+ async with aiohttp.ClientSession(timeout=timeout) as session, session.post(
195
+ f"{self.server_endpoint}/v1/chat/completions",
196
+ json=payload
197
+ ) as res:
198
+ if res.status != 200:
199
+ UserMessage(f"Request failed with status code: {res.status}", raise_with=ValueError)
200
+ return await res.json()
187
201
 
188
202
  return await _make_request()
189
203
 
204
+ @classmethod
205
+ def _extract_thinking(cls, response):
206
+ """Extract reasoning traces from llama.cpp responses."""
207
+ if not isinstance(response, dict):
208
+ return None
209
+ choices = response.get('choices', [])
210
+ if not isinstance(choices, list) or not choices:
211
+ return None
212
+ for choice in choices:
213
+ if isinstance(choice, dict) and isinstance(choice.get('message'), dict):
214
+ return choice['message'].get('reasoning_content')
215
+ return None
216
+
190
217
  def forward(self, argument):
191
218
  payload = self._prepare_request_payload(argument)
192
219
 
@@ -196,21 +223,63 @@ class LlamaCppEngine(Engine):
196
223
  try:
197
224
  res = loop.run_until_complete(self._arequest(payload))
198
225
  except Exception as e:
199
- CustomUserWarning(f'Error during generation. Caused by: {e}', raise_with=ValueError)
226
+ UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
200
227
 
201
228
  metadata = {'raw_output': res}
202
229
 
230
+ if payload.get('tools'):
231
+ metadata = self._process_tool_calls(res, metadata)
232
+
233
+ thinking = self._extract_thinking(res)
234
+ if thinking:
235
+ metadata['thinking'] = thinking
236
+
203
237
  output = [r['message']['content'] for r in res['choices']]
204
238
  output = output if isinstance(argument.prop.prepared_input, list) else output[0]
205
239
 
206
240
  return output, metadata
207
241
 
242
+ @staticmethod
243
+ def _process_tool_calls(res, metadata):
244
+ choices = res.get('choices') if isinstance(res, dict) else None
245
+ if not choices:
246
+ return metadata
247
+ hit = False
248
+ for choice in choices:
249
+ if not isinstance(choice, dict):
250
+ continue
251
+ message = choice.get('message') or {}
252
+ tool_calls = message.get('tool_calls') or []
253
+ if not tool_calls:
254
+ continue
255
+ for tool_call in tool_calls:
256
+ if not isinstance(tool_call, dict):
257
+ continue
258
+ function = tool_call.get('function') or {}
259
+ if hit:
260
+ UserMessage("Multiple function calls detected in the response but only the first one will be processed.")
261
+ return metadata
262
+ arguments = function.get('arguments')
263
+ try:
264
+ args_dict = json.loads(arguments) if isinstance(arguments, str) else arguments or {}
265
+ except json.JSONDecodeError:
266
+ args_dict = {}
267
+ metadata['function_call'] = {
268
+ 'name': function.get('name'),
269
+ 'arguments': args_dict or {}
270
+ }
271
+ hit = True
272
+ break
273
+ if hit:
274
+ break
275
+ return metadata
276
+
208
277
  def _prepare_raw_input(self, argument):
209
278
  if not argument.prop.processed_input:
210
- CustomUserWarning('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
279
+ UserMessage('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
211
280
  value = argument.prop.processed_input
212
- if type(value) != list:
213
- if type(value) != dict:
281
+ if not isinstance(value, list):
282
+ if not isinstance(value, dict):
214
283
  value = {'role': 'user', 'content': str(value)}
215
284
  value = [value]
216
285
  return value
@@ -242,17 +311,17 @@ class LlamaCppEngine(Engine):
242
311
 
243
312
  payload = argument.prop.payload
244
313
  if argument.prop.payload:
245
- user += f"<ADDITIONAL_CONTEXT/>\n{str(payload)}\n\n"
314
+ user += f"<ADDITIONAL_CONTEXT/>\n{payload!s}\n\n"
246
315
 
247
316
  examples: list[str] = argument.prop.examples
248
317
  if examples and len(examples) > 0:
249
- user += f"<EXAMPLES/>\n{str(examples)}\n\n"
318
+ user += f"<EXAMPLES/>\n{examples!s}\n\n"
250
319
 
251
320
  if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
252
- user += f"<INSTRUCTION/>\n{str(argument.prop.prompt)}\n\n"
321
+ user += f"<INSTRUCTION/>\n{argument.prop.prompt!s}\n\n"
253
322
 
254
323
  if argument.prop.template_suffix:
255
- user += f" You will only generate content for the placeholder `{str(argument.prop.template_suffix)}` following the instructions and the provided context information.\n\n"
324
+ user += f" You will only generate content for the placeholder `{argument.prop.template_suffix!s}` following the instructions and the provided context information.\n\n"
256
325
 
257
326
  user += str(argument.prop.processed_input)
258
327