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.
- symai/__init__.py +96 -64
- symai/backend/base.py +93 -80
- symai/backend/engines/drawing/engine_bfl.py +12 -11
- symai/backend/engines/drawing/engine_gpt_image.py +108 -87
- symai/backend/engines/embedding/engine_llama_cpp.py +25 -28
- symai/backend/engines/embedding/engine_openai.py +3 -5
- symai/backend/engines/execute/engine_python.py +6 -5
- symai/backend/engines/files/engine_io.py +74 -67
- symai/backend/engines/imagecaptioning/engine_blip2.py +3 -3
- symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +54 -38
- symai/backend/engines/index/engine_pinecone.py +23 -24
- symai/backend/engines/index/engine_vectordb.py +16 -14
- symai/backend/engines/lean/engine_lean4.py +38 -34
- symai/backend/engines/neurosymbolic/__init__.py +41 -13
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +262 -182
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +263 -191
- symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +53 -49
- symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +212 -211
- symai/backend/engines/neurosymbolic/engine_groq.py +87 -63
- symai/backend/engines/neurosymbolic/engine_huggingface.py +21 -24
- symai/backend/engines/neurosymbolic/engine_llama_cpp.py +117 -48
- symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +256 -229
- symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +270 -150
- symai/backend/engines/ocr/engine_apilayer.py +6 -8
- symai/backend/engines/output/engine_stdout.py +1 -4
- symai/backend/engines/search/engine_openai.py +7 -7
- symai/backend/engines/search/engine_perplexity.py +5 -5
- symai/backend/engines/search/engine_serpapi.py +12 -14
- symai/backend/engines/speech_to_text/engine_local_whisper.py +20 -27
- symai/backend/engines/symbolic/engine_wolframalpha.py +3 -3
- symai/backend/engines/text_to_speech/engine_openai.py +5 -7
- symai/backend/engines/text_vision/engine_clip.py +7 -11
- symai/backend/engines/userinput/engine_console.py +3 -3
- symai/backend/engines/webscraping/engine_requests.py +81 -48
- symai/backend/mixin/__init__.py +13 -0
- symai/backend/mixin/anthropic.py +4 -2
- symai/backend/mixin/deepseek.py +2 -0
- symai/backend/mixin/google.py +2 -0
- symai/backend/mixin/openai.py +11 -3
- symai/backend/settings.py +83 -16
- symai/chat.py +101 -78
- symai/collect/__init__.py +7 -1
- symai/collect/dynamic.py +77 -69
- symai/collect/pipeline.py +35 -27
- symai/collect/stats.py +75 -63
- symai/components.py +198 -169
- symai/constraints.py +15 -12
- symai/core.py +698 -359
- symai/core_ext.py +32 -34
- symai/endpoints/api.py +80 -73
- symai/extended/.DS_Store +0 -0
- symai/extended/__init__.py +46 -12
- symai/extended/api_builder.py +11 -8
- symai/extended/arxiv_pdf_parser.py +13 -12
- symai/extended/bibtex_parser.py +2 -3
- symai/extended/conversation.py +101 -90
- symai/extended/document.py +17 -10
- symai/extended/file_merger.py +18 -13
- symai/extended/graph.py +18 -13
- symai/extended/html_style_template.py +2 -4
- symai/extended/interfaces/blip_2.py +1 -2
- symai/extended/interfaces/clip.py +1 -2
- symai/extended/interfaces/console.py +7 -1
- symai/extended/interfaces/dall_e.py +1 -1
- symai/extended/interfaces/flux.py +1 -1
- symai/extended/interfaces/gpt_image.py +1 -1
- symai/extended/interfaces/input.py +1 -1
- symai/extended/interfaces/llava.py +0 -1
- symai/extended/interfaces/naive_vectordb.py +7 -8
- symai/extended/interfaces/naive_webscraping.py +1 -1
- symai/extended/interfaces/ocr.py +1 -1
- symai/extended/interfaces/pinecone.py +6 -5
- symai/extended/interfaces/serpapi.py +1 -1
- symai/extended/interfaces/terminal.py +2 -3
- symai/extended/interfaces/tts.py +1 -1
- symai/extended/interfaces/whisper.py +1 -1
- symai/extended/interfaces/wolframalpha.py +1 -1
- symai/extended/metrics/__init__.py +11 -1
- symai/extended/metrics/similarity.py +11 -13
- symai/extended/os_command.py +17 -16
- symai/extended/packages/__init__.py +29 -3
- symai/extended/packages/symdev.py +19 -16
- symai/extended/packages/sympkg.py +12 -9
- symai/extended/packages/symrun.py +21 -19
- symai/extended/repo_cloner.py +11 -10
- symai/extended/seo_query_optimizer.py +1 -2
- symai/extended/solver.py +20 -23
- symai/extended/summarizer.py +4 -3
- symai/extended/taypan_interpreter.py +10 -12
- symai/extended/vectordb.py +99 -82
- symai/formatter/__init__.py +9 -1
- symai/formatter/formatter.py +12 -16
- symai/formatter/regex.py +62 -63
- symai/functional.py +176 -122
- symai/imports.py +136 -127
- symai/interfaces.py +56 -27
- symai/memory.py +14 -13
- symai/misc/console.py +49 -39
- symai/misc/loader.py +5 -3
- symai/models/__init__.py +17 -1
- symai/models/base.py +269 -181
- symai/models/errors.py +0 -1
- symai/ops/__init__.py +32 -22
- symai/ops/measures.py +11 -15
- symai/ops/primitives.py +348 -228
- symai/post_processors.py +32 -28
- symai/pre_processors.py +39 -41
- symai/processor.py +6 -4
- symai/prompts.py +59 -45
- symai/server/huggingface_server.py +23 -20
- symai/server/llama_cpp_server.py +7 -5
- symai/shell.py +3 -4
- symai/shellsv.py +499 -375
- symai/strategy.py +517 -287
- symai/symbol.py +111 -116
- symai/utils.py +42 -36
- {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/METADATA +4 -2
- symbolicai-1.0.0.dist-info/RECORD +163 -0
- symbolicai-0.20.2.dist-info/RECORD +0 -162
- {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/WHEEL +0 -0
- {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/entry_points.txt +0 -0
- {symbolicai-0.20.2.dist-info → symbolicai-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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,
|
|
114
|
+
def compute_required_tokens(self, _messages) -> int:
|
|
116
115
|
#@TODO: quite non-trivial how to handle this with the llama.cpp server
|
|
117
|
-
|
|
116
|
+
UserMessage('Not implemented for llama.cpp!', raise_with=NotImplementedError)
|
|
118
117
|
|
|
119
|
-
def compute_remaining_tokens(self,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
213
|
-
if
|
|
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{
|
|
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{
|
|
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{
|
|
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 `{
|
|
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
|
|