symbolicai 0.21.0__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 +20 -24
  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 +44 -46
  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 +173 -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.21.0.dist-info → symbolicai-1.0.0.dist-info}/METADATA +4 -2
  118. symbolicai-1.0.0.dist-info/RECORD +163 -0
  119. symbolicai-0.21.0.dist-info/RECORD +0 -162
  120. {symbolicai-0.21.0.dist-info → symbolicai-1.0.0.dist-info}/WHEEL +0 -0
  121. {symbolicai-0.21.0.dist-info → symbolicai-1.0.0.dist-info}/entry_points.txt +0 -0
  122. {symbolicai-0.21.0.dist-info → symbolicai-1.0.0.dist-info}/licenses/LICENSE +0 -0
  123. {symbolicai-0.21.0.dist-info → symbolicai-1.0.0.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ import openai
7
7
 
8
8
  from ....components import SelfPrompt
9
9
  from ....core_ext import retry
10
- from ....utils import CustomUserWarning
10
+ from ....utils import UserMessage
11
11
  from ...base import Engine
12
12
  from ...settings import SYMAI_CONFIG
13
13
 
@@ -18,6 +18,15 @@ logging.getLogger("httpx").setLevel(logging.ERROR)
18
18
  logging.getLogger("httpcore").setLevel(logging.ERROR)
19
19
 
20
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
+
21
30
  class GroqEngine(Engine):
22
31
  def __init__(self, api_key: str | None = None, model: str | None = None):
23
32
  super().__init__()
@@ -36,7 +45,7 @@ class GroqEngine(Engine):
36
45
  try:
37
46
  self.client = openai.OpenAI(api_key=openai.api_key, base_url="https://api.groq.com/openai/v1")
38
47
  except Exception as e:
39
- CustomUserWarning(f'Failed to initialize OpenAI client. Please check your OpenAI library version. Caused by: {e}', raise_with=ValueError)
48
+ UserMessage(f'Failed to initialize OpenAI client. Please check your OpenAI library version. Caused by: {e}', raise_with=ValueError)
40
49
 
41
50
  def id(self) -> str:
42
51
  if self.config.get('NEUROSYMBOLIC_ENGINE_MODEL') and \
@@ -53,11 +62,11 @@ class GroqEngine(Engine):
53
62
  if 'seed' in kwargs:
54
63
  self.seed = kwargs['seed']
55
64
 
56
- def compute_required_tokens(self, messages):
57
- raise NotImplementedError("Token counting not implemented for this engine.")
65
+ def compute_required_tokens(self, _messages):
66
+ UserMessage("Token counting not implemented for this engine.", raise_with=NotImplementedError)
58
67
 
59
- def compute_remaining_tokens(self, prompts: list) -> int:
60
- raise NotImplementedError("Token counting not implemented for this engine.")
68
+ def compute_remaining_tokens(self, _prompts: list) -> int:
69
+ UserMessage("Token counting not implemented for this engine.", raise_with=NotImplementedError)
61
70
 
62
71
  def _handle_prefix(self, model_name: str) -> str:
63
72
  """Handle prefix for model name."""
@@ -82,7 +91,7 @@ class GroqEngine(Engine):
82
91
  thinking_content = None
83
92
 
84
93
  cleaned_content = re.sub(think_pattern, '', content, flags=re.DOTALL).strip()
85
- cleaned_output = [cleaned_content] + output[1:]
94
+ cleaned_output = [cleaned_content, *output[1:]]
86
95
 
87
96
  return thinking_content, cleaned_output
88
97
 
@@ -100,9 +109,9 @@ class GroqEngine(Engine):
100
109
  except Exception as e:
101
110
  if openai.api_key is None or openai.api_key == '':
102
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.'
103
- logging.error(msg)
112
+ UserMessage(msg)
104
113
  if self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] is None or self.config['NEUROSYMBOLIC_ENGINE_API_KEY'] == '':
105
- CustomUserWarning(msg, raise_with=ValueError)
114
+ UserMessage(msg, raise_with=ValueError)
106
115
  openai.api_key = self.config['NEUROSYMBOLIC_ENGINE_API_KEY']
107
116
 
108
117
  callback = self.client.chat.completions.create
@@ -111,7 +120,7 @@ class GroqEngine(Engine):
111
120
  if except_remedy is not None:
112
121
  res = except_remedy(self, e, callback, argument)
113
122
  else:
114
- CustomUserWarning(f'Error during generation. Caused by: {e}', raise_with=ValueError)
123
+ UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
115
124
 
116
125
  metadata = {'raw_output': res}
117
126
  if payload.get('tools'):
@@ -126,11 +135,11 @@ class GroqEngine(Engine):
126
135
 
127
136
  def _prepare_raw_input(self, argument):
128
137
  if not argument.prop.processed_input:
129
- CustomUserWarning('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
138
+ UserMessage('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
130
139
  value = argument.prop.processed_input
131
140
  # convert to dict if not already
132
- if type(value) != list:
133
- if type(value) != dict:
141
+ if not isinstance(value, list):
142
+ if not isinstance(value, dict):
134
143
  value = {'role': 'user', 'content': str(value)}
135
144
  value = [value]
136
145
  return value
@@ -139,18 +148,32 @@ class GroqEngine(Engine):
139
148
  if argument.prop.raw_input:
140
149
  argument.prop.prepared_input = self._prepare_raw_input(argument)
141
150
  return
151
+ self._validate_response_format(argument)
142
152
 
143
- _non_verbose_output = """<META_INSTRUCTION/>\nYou do not output anything else, like verbose preambles or post explanation, such as "Sure, let me...", "Hope that was helpful...", "Yes, I can help you with that...", etc. Consider well formatted output, e.g. for sentences use punctuation, spaces etc. or for code use indentation, etc. Never add meta instructions information to your output!\n\n"""
144
- user: str = ""
145
- system: str = ""
153
+ system = self._build_system_message(argument)
154
+ user_content = self._build_user_content(argument)
155
+ user_prompt = {"role": "user", "content": user_content}
156
+ system, user_prompt = self._apply_self_prompt_if_needed(argument, system, user_prompt)
146
157
 
147
- if argument.prop.suppress_verbose_output:
148
- system += _non_verbose_output
149
- system = f'{system}\n' if system and len(system) > 0 else ''
158
+ argument.prop.prepared_input = [
159
+ { "role": "system", "content": system },
160
+ user_prompt,
161
+ ]
150
162
 
163
+ def _validate_response_format(self, argument) -> None:
151
164
  if argument.prop.response_format:
152
- _rsp_fmt = argument.prop.response_format
153
- assert _rsp_fmt.get('type') is not None, 'Expected format `{ "type": "json_object" }`! We are using the OpenAI compatible API for Groq. See more here: https://console.groq.com/docs/tool-use'
165
+ response_format = argument.prop.response_format
166
+ assert response_format.get('type') is not None, (
167
+ 'Expected format `{ "type": "json_object" }`! We are using the OpenAI compatible API for Groq. '
168
+ "See more here: https://console.groq.com/docs/tool-use"
169
+ )
170
+
171
+ def _build_system_message(self, argument) -> str:
172
+ system: str = ""
173
+ if argument.prop.suppress_verbose_output:
174
+ system += _NON_VERBOSE_OUTPUT
175
+ if system:
176
+ system = f"{system}\n"
154
177
 
155
178
  ref = argument.prop.instance
156
179
  static_ctxt, dyn_ctxt = ref.global_context
@@ -160,62 +183,61 @@ class GroqEngine(Engine):
160
183
  if len(dyn_ctxt) > 0:
161
184
  system += f"<DYNAMIC CONTEXT/>\n{dyn_ctxt}\n\n"
162
185
 
163
- payload = argument.prop.payload
164
186
  if argument.prop.payload:
165
- system += f"<ADDITIONAL CONTEXT/>\n{str(payload)}\n\n"
187
+ system += f"<ADDITIONAL CONTEXT/>\n{argument.prop.payload!s}\n\n"
166
188
 
167
189
  examples = argument.prop.examples
168
190
  if examples and len(examples) > 0:
169
- system += f"<EXAMPLES/>\n{str(examples)}\n\n"
191
+ system += f"<EXAMPLES/>\n{examples!s}\n\n"
170
192
 
171
193
  if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
172
194
  val = str(argument.prop.prompt)
173
195
  system += f"<INSTRUCTION/>\n{val}\n\n"
174
196
 
175
- suffix: str = str(argument.prop.processed_input)
176
- user += f"{suffix}"
177
-
178
197
  if argument.prop.template_suffix:
179
- system += f' You will only generate content for the placeholder `{str(argument.prop.template_suffix)}` following the instructions and the provided context information.\n\n'
198
+ system += (
199
+ " You will only generate content for the placeholder "
200
+ f"`{argument.prop.template_suffix!s}` following the instructions and the provided context information.\n\n"
201
+ )
202
+
203
+ return system
180
204
 
181
- user_prompt = { "role": "user", "content": user }
205
+ def _build_user_content(self, argument) -> str:
206
+ return str(argument.prop.processed_input)
182
207
 
183
- # First check if the `Symbol` instance has the flag set, otherwise check if it was passed as an argument to a method
208
+ def _apply_self_prompt_if_needed(self, argument, system, user_prompt):
184
209
  if argument.prop.instance._kwargs.get('self_prompt', False) or argument.prop.self_prompt:
185
210
  self_prompter = SelfPrompt()
186
-
187
- res = self_prompter({'user': user, 'system': system})
211
+ res = self_prompter({'user': user_prompt['content'], 'system': system})
188
212
  if res is None:
189
- CustomUserWarning("Self-prompting failed!", raise_with=ValueError)
190
-
191
- user_prompt = { "role": "user", "content": res['user'] }
192
- system = res['system']
193
-
194
- argument.prop.prepared_input = [
195
- { "role": "system", "content": system },
196
- user_prompt,
197
- ]
213
+ UserMessage("Self-prompting failed!", raise_with=ValueError)
214
+ return res['system'], {"role": "user", "content": res['user']}
215
+ return system, user_prompt
198
216
 
199
217
  def _process_function_calls(self, res, metadata):
200
218
  hit = False
201
- if hasattr(res, 'choices') and res.choices:
202
- choice = res.choices[0]
203
- if hasattr(choice, 'message') and choice.message:
204
- if hasattr(choice.message, 'tool_calls') and choice.message.tool_calls:
205
- for tool_call in choice.message.tool_calls:
206
- if hasattr(tool_call, 'function') and tool_call.function:
207
- if hit:
208
- CustomUserWarning("Multiple function calls detected in the response but only the first one will be processed.")
209
- break
210
- try:
211
- args_dict = json.loads(tool_call.function.arguments)
212
- except json.JSONDecodeError:
213
- args_dict = {}
214
- metadata['function_call'] = {
215
- 'name': tool_call.function.name,
216
- 'arguments': args_dict
217
- }
218
- hit = True
219
+ if (
220
+ hasattr(res, 'choices')
221
+ and res.choices
222
+ and hasattr(res.choices[0], 'message')
223
+ and res.choices[0].message
224
+ and hasattr(res.choices[0].message, 'tool_calls')
225
+ and res.choices[0].message.tool_calls
226
+ ):
227
+ for tool_call in res.choices[0].message.tool_calls:
228
+ if hasattr(tool_call, 'function') and tool_call.function:
229
+ if hit:
230
+ UserMessage("Multiple function calls detected in the response but only the first one will be processed.")
231
+ break
232
+ try:
233
+ args_dict = json.loads(tool_call.function.arguments)
234
+ except json.JSONDecodeError:
235
+ args_dict = {}
236
+ metadata['function_call'] = {
237
+ 'name': tool_call.function.name,
238
+ 'arguments': args_dict
239
+ }
240
+ hit = True
219
241
  return metadata
220
242
 
221
243
  def _prepare_request_payload(self, messages, argument):
@@ -231,12 +253,12 @@ class GroqEngine(Engine):
231
253
  "stream_options",
232
254
  ]:
233
255
  if param in kwargs:
234
- CustomUserWarning(f"The parameter {param} is not supported by the Groq API. It will be ignored.")
256
+ UserMessage(f"The parameter {param} is not supported by the Groq API. It will be ignored.")
235
257
  del kwargs[param]
236
258
 
237
259
  n = kwargs.get('n', 1)
238
260
  if n > 1:
239
- CustomUserWarning("If N is supplied, it must be equal to 1. We default to 1 to not crash your program.")
261
+ UserMessage("If N is supplied, it must be equal to 1. We default to 1 to not crash your program.")
240
262
  n = 1
241
263
 
242
264
  # Handle Groq JSON-mode quirk: JSON Object Mode internally uses a constrainer tool.
@@ -244,8 +266,10 @@ class GroqEngine(Engine):
244
266
  tool_choice = kwargs.get('tool_choice', 'auto' if kwargs.get('tools') else 'none')
245
267
  tools = kwargs.get('tools')
246
268
  if response_format and isinstance(response_format, dict) and response_format.get('type') == 'json_object':
247
- if tool_choice in (None, 'none'): tool_choice = 'auto'
248
- if tools: tools = None
269
+ if tool_choice in (None, 'none'):
270
+ tool_choice = 'auto'
271
+ if tools:
272
+ tools = None
249
273
 
250
274
  payload = {
251
275
  "messages": messages,
@@ -1,12 +1,9 @@
1
- import json
2
1
  import logging
3
- import re
4
2
  from copy import deepcopy
5
- from typing import List, Optional
6
3
 
7
4
  import requests
8
5
 
9
- from ....utils import CustomUserWarning
6
+ from ....utils import UserMessage
10
7
  from ...base import Engine
11
8
  from ...settings import SYMAI_CONFIG, SYMSERVER_CONFIG
12
9
 
@@ -20,28 +17,28 @@ class HFTokenizer:
20
17
  _server_endpoint = f"http://{SYMSERVER_CONFIG.get('host')}:{SYMSERVER_CONFIG.get('port')}"
21
18
 
22
19
  @staticmethod
23
- def encode(text: str, add_special_tokens: bool = False) -> List[int]:
20
+ def encode(text: str, add_special_tokens: bool = False) -> list[int]:
24
21
  res = requests.post(f"{HFTokenizer._server_endpoint}/tokenize", json={
25
22
  "input": text,
26
23
  "add_special_tokens": add_special_tokens,
27
24
  })
28
25
 
29
26
  if res.status_code != 200:
30
- CustomUserWarning(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
27
+ UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
31
28
 
32
29
  res = res.json()
33
30
 
34
31
  return res['tokens']
35
32
 
36
33
  @staticmethod
37
- def decode(tokens: List[int], skip_special_tokens: bool = True) -> str:
34
+ def decode(tokens: list[int], skip_special_tokens: bool = True) -> str:
38
35
  res = requests.post(f"{HFTokenizer._server_endpoint}/detokenize", json={
39
36
  "tokens": tokens,
40
37
  "skip_special_tokens": skip_special_tokens,
41
38
  })
42
39
 
43
40
  if res.status_code != 200:
44
- CustomUserWarning(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
41
+ UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
45
42
 
46
43
  res = res.json()
47
44
 
@@ -49,7 +46,7 @@ class HFTokenizer:
49
46
 
50
47
 
51
48
  class HFEngine(Engine):
52
- def __init__(self, model: Optional[str] = None):
49
+ def __init__(self, model: str | None = None):
53
50
  super().__init__()
54
51
  self.config = deepcopy(SYMAI_CONFIG)
55
52
  # In case we use EngineRepository.register to inject the api_key and model => dynamically change the engine at runtime
@@ -58,7 +55,7 @@ class HFEngine(Engine):
58
55
  if self.id() != 'neurosymbolic':
59
56
  return
60
57
  if not SYMSERVER_CONFIG.get('online'):
61
- CustomUserWarning('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)
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)
62
59
  self.server_endpoint = f"http://{SYMSERVER_CONFIG.get('host')}:{SYMSERVER_CONFIG.get('port')}"
63
60
  self.tokenizer = HFTokenizer # backwards compatibility with how we handle tokenization, i.e. self.tokenizer().encode(...)
64
61
  self.name = self.__class__.__name__
@@ -77,10 +74,10 @@ class HFEngine(Engine):
77
74
  if 'except_remedy' in kwargs:
78
75
  self.except_remedy = kwargs['except_remedy']
79
76
 
80
- def compute_required_tokens(self, messages) -> int:
81
- CustomUserWarning('Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.', raise_with=NotImplementedError)
82
- def compute_remaining_tokens(self, prompts: list) -> int:
83
- CustomUserWarning('Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.', raise_with=NotImplementedError)
77
+ def compute_required_tokens(self, _messages) -> int:
78
+ UserMessage('Not implemented for HFEngine. Please use the tokenizer directly to compute tokens.', raise_with=NotImplementedError)
79
+ 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)
84
81
  def forward(self, argument):
85
82
  kwargs = argument.kwargs
86
83
  prompts = argument.prop.prepared_input
@@ -117,7 +114,7 @@ class HFEngine(Engine):
117
114
  })
118
115
 
119
116
  if res.status_code != 200:
120
- CustomUserWarning(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
117
+ UserMessage(f"Request failed with status code: {res.status_code}", raise_with=ValueError)
121
118
 
122
119
  res = res.json()
123
120
 
@@ -125,7 +122,7 @@ class HFEngine(Engine):
125
122
  if except_remedy is not None:
126
123
  res = except_remedy(self, e, argument)
127
124
  else:
128
- CustomUserWarning(f'Error during generation. Caused by: {e}', raise_with=ValueError)
125
+ UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
129
126
 
130
127
  metadata = {'raw_output': res}
131
128
 
@@ -135,10 +132,10 @@ class HFEngine(Engine):
135
132
 
136
133
  def _prepare_raw_input(self, argument):
137
134
  if not argument.prop.processed_input:
138
- CustomUserWarning('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
135
+ UserMessage('Need to provide a prompt instruction to the engine if raw_input is enabled.', raise_with=ValueError)
139
136
  value = argument.prop.processed_input
140
- if type(value) != list:
141
- if type(value) != dict:
137
+ if not isinstance(value, list):
138
+ if not isinstance(value, dict):
142
139
  value = {'role': 'user', 'content': str(value)}
143
140
  value = [value]
144
141
  return value
@@ -167,17 +164,17 @@ class HFEngine(Engine):
167
164
 
168
165
  payload = argument.prop.payload
169
166
  if argument.prop.payload:
170
- user += f"<ADDITIONAL_CONTEXT/>\n{str(payload)}\n\n"
167
+ user += f"<ADDITIONAL_CONTEXT/>\n{payload!s}\n\n"
171
168
 
172
- examples: List[str] = argument.prop.examples
169
+ examples: list[str] = argument.prop.examples
173
170
  if examples and len(examples) > 0:
174
- user += f"<EXAMPLES/>\n{str(examples)}\n\n"
171
+ user += f"<EXAMPLES/>\n{examples!s}\n\n"
175
172
 
176
173
  if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
177
- user += f"<INSTRUCTION/>\n{str(argument.prop.prompt)}\n\n"
174
+ user += f"<INSTRUCTION/>\n{argument.prop.prompt!s}\n\n"
178
175
 
179
176
  if argument.prop.template_suffix:
180
- 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"
177
+ 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"
181
178
 
182
179
  user += str(argument.prop.processed_input)
183
180
 
@@ -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}/tokenize", json={
30
- "content": 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}/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['content']
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()
@@ -192,14 +191,13 @@ class LlamaCppEngine(Engine):
192
191
  sock_connect=self.timeout_params['connect'],
193
192
  sock_read=self.timeout_params['read']
194
193
  )
195
- async with aiohttp.ClientSession(timeout=timeout) as session:
196
- async with session.post(
197
- f"{self.server_endpoint}/v1/chat/completions",
198
- json=payload
199
- ) as res:
200
- if res.status != 200:
201
- CustomUserWarning(f"Request failed with status code: {res.status}", raise_with=ValueError)
202
- 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()
203
201
 
204
202
  return await _make_request()
205
203
 
@@ -225,7 +223,7 @@ class LlamaCppEngine(Engine):
225
223
  try:
226
224
  res = loop.run_until_complete(self._arequest(payload))
227
225
  except Exception as e:
228
- CustomUserWarning(f'Error during generation. Caused by: {e}', raise_with=ValueError)
226
+ UserMessage(f'Error during generation. Caused by: {e}', raise_with=ValueError)
229
227
 
230
228
  metadata = {'raw_output': res}
231
229
 
@@ -259,7 +257,7 @@ class LlamaCppEngine(Engine):
259
257
  continue
260
258
  function = tool_call.get('function') or {}
261
259
  if hit:
262
- CustomUserWarning("Multiple function calls detected in the response but only the first one will be processed.")
260
+ UserMessage("Multiple function calls detected in the response but only the first one will be processed.")
263
261
  return metadata
264
262
  arguments = function.get('arguments')
265
263
  try:
@@ -278,10 +276,10 @@ class LlamaCppEngine(Engine):
278
276
 
279
277
  def _prepare_raw_input(self, argument):
280
278
  if not argument.prop.processed_input:
281
- 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)
282
280
  value = argument.prop.processed_input
283
- if type(value) != list:
284
- if type(value) != dict:
281
+ if not isinstance(value, list):
282
+ if not isinstance(value, dict):
285
283
  value = {'role': 'user', 'content': str(value)}
286
284
  value = [value]
287
285
  return value
@@ -313,17 +311,17 @@ class LlamaCppEngine(Engine):
313
311
 
314
312
  payload = argument.prop.payload
315
313
  if argument.prop.payload:
316
- user += f"<ADDITIONAL_CONTEXT/>\n{str(payload)}\n\n"
314
+ user += f"<ADDITIONAL_CONTEXT/>\n{payload!s}\n\n"
317
315
 
318
316
  examples: list[str] = argument.prop.examples
319
317
  if examples and len(examples) > 0:
320
- user += f"<EXAMPLES/>\n{str(examples)}\n\n"
318
+ user += f"<EXAMPLES/>\n{examples!s}\n\n"
321
319
 
322
320
  if argument.prop.prompt is not None and len(argument.prop.prompt) > 0:
323
- user += f"<INSTRUCTION/>\n{str(argument.prop.prompt)}\n\n"
321
+ user += f"<INSTRUCTION/>\n{argument.prop.prompt!s}\n\n"
324
322
 
325
323
  if argument.prop.template_suffix:
326
- 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"
327
325
 
328
326
  user += str(argument.prop.processed_input)
329
327