symbolicai 1.0.0__py3-none-any.whl → 1.1.1__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 (129) 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 +35 -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/__init__.py +4 -0
  39. symai/backend/mixin/anthropic.py +48 -40
  40. symai/backend/mixin/cerebras.py +9 -0
  41. symai/backend/mixin/deepseek.py +4 -5
  42. symai/backend/mixin/google.py +5 -4
  43. symai/backend/mixin/groq.py +2 -4
  44. symai/backend/mixin/openai.py +132 -110
  45. symai/backend/settings.py +14 -14
  46. symai/chat.py +164 -94
  47. symai/collect/dynamic.py +13 -11
  48. symai/collect/pipeline.py +39 -31
  49. symai/collect/stats.py +109 -69
  50. symai/components.py +578 -238
  51. symai/constraints.py +14 -5
  52. symai/core.py +1495 -1210
  53. symai/core_ext.py +55 -50
  54. symai/endpoints/api.py +113 -58
  55. symai/extended/api_builder.py +22 -17
  56. symai/extended/arxiv_pdf_parser.py +13 -5
  57. symai/extended/bibtex_parser.py +8 -4
  58. symai/extended/conversation.py +88 -69
  59. symai/extended/document.py +40 -27
  60. symai/extended/file_merger.py +45 -7
  61. symai/extended/graph.py +38 -24
  62. symai/extended/html_style_template.py +17 -11
  63. symai/extended/interfaces/blip_2.py +1 -1
  64. symai/extended/interfaces/clip.py +4 -2
  65. symai/extended/interfaces/console.py +5 -3
  66. symai/extended/interfaces/dall_e.py +3 -1
  67. symai/extended/interfaces/file.py +2 -0
  68. symai/extended/interfaces/flux.py +3 -1
  69. symai/extended/interfaces/gpt_image.py +15 -6
  70. symai/extended/interfaces/input.py +2 -1
  71. symai/extended/interfaces/llava.py +1 -1
  72. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
  73. symai/extended/interfaces/naive_vectordb.py +2 -2
  74. symai/extended/interfaces/ocr.py +4 -2
  75. symai/extended/interfaces/openai_search.py +2 -0
  76. symai/extended/interfaces/parallel.py +30 -0
  77. symai/extended/interfaces/perplexity.py +2 -0
  78. symai/extended/interfaces/pinecone.py +6 -4
  79. symai/extended/interfaces/python.py +2 -0
  80. symai/extended/interfaces/serpapi.py +2 -0
  81. symai/extended/interfaces/terminal.py +0 -1
  82. symai/extended/interfaces/tts.py +2 -1
  83. symai/extended/interfaces/whisper.py +2 -1
  84. symai/extended/interfaces/wolframalpha.py +1 -0
  85. symai/extended/metrics/__init__.py +1 -1
  86. symai/extended/metrics/similarity.py +5 -2
  87. symai/extended/os_command.py +31 -22
  88. symai/extended/packages/symdev.py +39 -34
  89. symai/extended/packages/sympkg.py +30 -27
  90. symai/extended/packages/symrun.py +46 -35
  91. symai/extended/repo_cloner.py +10 -9
  92. symai/extended/seo_query_optimizer.py +15 -12
  93. symai/extended/solver.py +104 -76
  94. symai/extended/summarizer.py +8 -7
  95. symai/extended/taypan_interpreter.py +10 -9
  96. symai/extended/vectordb.py +28 -15
  97. symai/formatter/formatter.py +39 -31
  98. symai/formatter/regex.py +46 -44
  99. symai/functional.py +184 -86
  100. symai/imports.py +85 -51
  101. symai/interfaces.py +1 -1
  102. symai/memory.py +33 -24
  103. symai/menu/screen.py +28 -19
  104. symai/misc/console.py +27 -27
  105. symai/misc/loader.py +4 -3
  106. symai/models/base.py +147 -76
  107. symai/models/errors.py +1 -1
  108. symai/ops/__init__.py +1 -1
  109. symai/ops/measures.py +17 -14
  110. symai/ops/primitives.py +933 -635
  111. symai/post_processors.py +28 -24
  112. symai/pre_processors.py +58 -52
  113. symai/processor.py +15 -9
  114. symai/prompts.py +714 -649
  115. symai/server/huggingface_server.py +115 -32
  116. symai/server/llama_cpp_server.py +14 -6
  117. symai/server/qdrant_server.py +206 -0
  118. symai/shell.py +98 -39
  119. symai/shellsv.py +307 -223
  120. symai/strategy.py +135 -81
  121. symai/symbol.py +276 -225
  122. symai/utils.py +62 -46
  123. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/METADATA +19 -9
  124. symbolicai-1.1.1.dist-info/RECORD +169 -0
  125. symbolicai-1.0.0.dist-info/RECORD +0 -163
  126. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/WHEEL +0 -0
  127. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/entry_points.txt +0 -0
  128. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/licenses/LICENSE +0 -0
  129. {symbolicai-1.0.0.dist-info → symbolicai-1.1.1.dist-info}/top_level.txt +0 -0
@@ -17,10 +17,10 @@ logging.getLogger("httpcore").setLevel(logging.ERROR)
17
17
  class SearchResult(Result):
18
18
  def __init__(self, value, **kwargs) -> None:
19
19
  super().__init__(value, **kwargs)
20
- if value.get('error'):
21
- UserMessage(value['error'], raise_with=ValueError)
20
+ if value.get("error"):
21
+ UserMessage(value["error"], raise_with=ValueError)
22
22
  try:
23
- self._value = value['choices'][0]['message']['content']
23
+ self._value = value["choices"][0]["message"]["content"]
24
24
  except Exception as e:
25
25
  self._value = None
26
26
  UserMessage(f"Failed to parse response: {e}", raise_with=ValueError)
@@ -42,73 +42,72 @@ class PerplexityEngine(Engine):
42
42
  def __init__(self):
43
43
  super().__init__()
44
44
  self.config = SYMAI_CONFIG
45
- self.api_key = self.config['SEARCH_ENGINE_API_KEY']
46
- self.model = self.config['SEARCH_ENGINE_MODEL']
45
+ self.api_key = self.config["SEARCH_ENGINE_API_KEY"]
46
+ self.model = self.config["SEARCH_ENGINE_MODEL"]
47
47
  self.name = self.__class__.__name__
48
48
 
49
49
  def id(self) -> str:
50
- if self.config.get('SEARCH_ENGINE_API_KEY') and self.config.get('SEARCH_ENGINE_MODEL').startswith("sonar"):
51
- return 'search'
50
+ if self.config.get("SEARCH_ENGINE_API_KEY") and self.config.get(
51
+ "SEARCH_ENGINE_MODEL"
52
+ ).startswith("sonar"):
53
+ return "search"
52
54
  return super().id() # default to unregistered
53
55
 
54
56
  def command(self, *args, **kwargs):
55
57
  super().command(*args, **kwargs)
56
- if 'SEARCH_ENGINE_API_KEY' in kwargs:
57
- self.api_key = kwargs['SEARCH_ENGINE_API_KEY']
58
- if 'SEARCH_ENGINE_MODEL' in kwargs:
59
- self.model = kwargs['SEARCH_ENGINE_MODEL']
58
+ if "SEARCH_ENGINE_API_KEY" in kwargs:
59
+ self.api_key = kwargs["SEARCH_ENGINE_API_KEY"]
60
+ if "SEARCH_ENGINE_MODEL" in kwargs:
61
+ self.model = kwargs["SEARCH_ENGINE_MODEL"]
60
62
 
61
63
  def forward(self, argument):
62
- messages = argument.prop.prepared_input
63
- kwargs = argument.kwargs
64
+ messages = argument.prop.prepared_input
65
+ kwargs = argument.kwargs
64
66
 
65
67
  payload = {
66
68
  "model": self.model,
67
69
  "messages": messages,
68
- "max_tokens": kwargs.get('max_tokens', None),
69
- "temperature": kwargs.get('temperature', 0.2),
70
- "top_p": kwargs.get('top_p', 0.9),
71
- "search_domain_filter": kwargs.get('search_domain_filter', []),
72
- "return_images": kwargs.get('return_images', False),
73
- "return_related_questions": kwargs.get('return_related_questions', False),
74
- "search_recency_filter": kwargs.get('search_recency_filter', "month"),
75
- "top_k": kwargs.get('top_k', 0),
76
- "stream": kwargs.get('stream', False),
77
- "presence_penalty": kwargs.get('presence_penalty', 0),
78
- "frequency_penalty": kwargs.get('frequency_penalty', 1),
79
- "response_format": kwargs.get('response_format', None)
70
+ "max_tokens": kwargs.get("max_tokens", None),
71
+ "temperature": kwargs.get("temperature", 0.2),
72
+ "top_p": kwargs.get("top_p", 0.9),
73
+ "search_domain_filter": kwargs.get("search_domain_filter", []),
74
+ "return_images": kwargs.get("return_images", False),
75
+ "return_related_questions": kwargs.get("return_related_questions", False),
76
+ "search_recency_filter": kwargs.get("search_recency_filter", "month"),
77
+ "top_k": kwargs.get("top_k", 0),
78
+ "stream": kwargs.get("stream", False),
79
+ "presence_penalty": kwargs.get("presence_penalty", 0),
80
+ "frequency_penalty": kwargs.get("frequency_penalty", 1),
81
+ "response_format": kwargs.get("response_format", None),
80
82
  }
81
- web_search_options = kwargs.get('web_search_options', None)
83
+ web_search_options = kwargs.get("web_search_options", None)
82
84
  if web_search_options is not None:
83
85
  payload["web_search_options"] = web_search_options
84
86
 
85
- headers = {
86
- "Authorization": f"Bearer {self.api_key}",
87
- "Content-Type": "application/json"
88
- }
87
+ headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
89
88
 
90
89
  try:
91
- res = requests.post("https://api.perplexity.ai/chat/completions", json=payload, headers=headers)
90
+ res = requests.post(
91
+ "https://api.perplexity.ai/chat/completions", json=payload, headers=headers
92
+ )
92
93
  res = SearchResult(res.json())
93
94
  except Exception as e:
94
95
  UserMessage(f"Failed to make request: {e}", raise_with=ValueError)
95
96
 
96
97
  metadata = {"raw_output": res.raw}
97
- output = [res]
98
+ output = [res]
98
99
 
99
100
  return output, metadata
100
101
 
101
102
  def prepare(self, argument):
102
- system_message = "You are a helpful AI assistant. Be precise and informative." if argument.kwargs.get('system_message') is None else argument.kwargs.get('system_message')
103
+ system_message = (
104
+ "You are a helpful AI assistant. Be precise and informative."
105
+ if argument.kwargs.get("system_message") is None
106
+ else argument.kwargs.get("system_message")
107
+ )
103
108
 
104
109
  res = [
105
- {
106
- "role": "system",
107
- "content": system_message
108
- },
109
- {
110
- "role": "user",
111
- "content": f"{argument.prop.query}"
112
- }
110
+ {"role": "system", "content": system_message},
111
+ {"role": "user", "content": f"{argument.prop.query}"},
113
112
  ]
114
113
  argument.prop.prepared_input = res
@@ -16,21 +16,21 @@ except ImportError:
16
16
  class SearchResult(Result):
17
17
  def __init__(self, value, **kwargs) -> None:
18
18
  super().__init__(value, **kwargs)
19
- if 'answer_box' in value and 'answer' in value['answer_box']:
20
- self._value = value['answer_box']['answer']
21
- elif 'answer_box' in value and 'snippet' in value['answer_box']:
22
- self._value = value['answer_box']['snippet']
23
- elif 'answer_box' in value and 'snippet_highlighted_words' in value['answer_box']:
24
- self._value = value['answer_box']["snippet_highlighted_words"][0]
25
- elif 'organic_results' in value and 'snippet' in value["organic_results"][0]:
26
- self._value = value["organic_results"][0]['snippet']
19
+ if "answer_box" in value and "answer" in value["answer_box"]:
20
+ self._value = value["answer_box"]["answer"]
21
+ elif "answer_box" in value and "snippet" in value["answer_box"]:
22
+ self._value = value["answer_box"]["snippet"]
23
+ elif "answer_box" in value and "snippet_highlighted_words" in value["answer_box"]:
24
+ self._value = value["answer_box"]["snippet_highlighted_words"][0]
25
+ elif "organic_results" in value and "snippet" in value["organic_results"][0]:
26
+ self._value = value["organic_results"][0]["snippet"]
27
27
  else:
28
28
  self._value = value
29
29
 
30
- if 'organic_results' in value:
31
- self.results = value['organic_results']
30
+ if "organic_results" in value:
31
+ self.results = value["organic_results"]
32
32
  if len(self.results) > 0:
33
- self.links = [r['link'] for r in self.results]
33
+ self.links = [r["link"] for r in self.results]
34
34
  else:
35
35
  self.links = []
36
36
  else:
@@ -48,30 +48,35 @@ class SerpApiEngine(Engine):
48
48
  def __init__(self):
49
49
  super().__init__()
50
50
  self.config = SYMAI_CONFIG
51
- self.api_key = self.config['SEARCH_ENGINE_API_KEY']
52
- self.engine = self.config['SEARCH_ENGINE_MODEL']
51
+ self.api_key = self.config["SEARCH_ENGINE_API_KEY"]
52
+ self.engine = self.config["SEARCH_ENGINE_MODEL"]
53
53
  self.name = self.__class__.__name__
54
54
 
55
55
  def id(self) -> str:
56
- if self.config.get('SEARCH_ENGINE_API_KEY') and self.config.get('SEARCH_ENGINE_MODEL') == "google": # only support Google for now
56
+ if (
57
+ self.config.get("SEARCH_ENGINE_API_KEY")
58
+ and self.config.get("SEARCH_ENGINE_MODEL") == "google"
59
+ ): # only support Google for now
57
60
  if GoogleSearch is None:
58
- UserMessage('SerpApi is not installed. Please install it with `pip install symbolicai[serpapi]`')
59
- return 'search'
60
- return super().id() # default to unregistered
61
+ UserMessage(
62
+ "SerpApi is not installed. Please install it with `pip install symbolicai[serpapi]`"
63
+ )
64
+ return "search"
65
+ return super().id() # default to unregistered
61
66
 
62
67
  def command(self, *args, **kwargs):
63
68
  super().command(*args, **kwargs)
64
- if 'SEARCH_ENGINE_API_KEY' in kwargs:
65
- self.api_key = kwargs['SEARCH_ENGINE_API_KEY']
66
- if 'SEARCH_ENGINE_MODEL' in kwargs:
67
- self.engine = kwargs['SEARCH_ENGINE_MODEL']
69
+ if "SEARCH_ENGINE_API_KEY" in kwargs:
70
+ self.api_key = kwargs["SEARCH_ENGINE_API_KEY"]
71
+ if "SEARCH_ENGINE_MODEL" in kwargs:
72
+ self.engine = kwargs["SEARCH_ENGINE_MODEL"]
68
73
 
69
74
  def forward(self, argument):
70
- queries = argument.prop.prepared_input
71
- kwargs = argument.kwargs
75
+ queries = argument.prop.prepared_input
76
+ kwargs = argument.kwargs
72
77
  queries_ = queries if isinstance(queries, list) else [queries]
73
- rsp = []
74
- engine = kwargs.get('engine', self.engine)
78
+ rsp = []
79
+ engine = kwargs.get("engine", self.engine)
75
80
 
76
81
  for q in queries_:
77
82
  query = {
@@ -80,11 +85,11 @@ class SerpApiEngine(Engine):
80
85
  "q": q,
81
86
  "google_domain": "google.com",
82
87
  "gl": "us",
83
- "hl": "en"
88
+ "hl": "en",
84
89
  }
85
90
 
86
91
  # send to Google
87
- with io.capture_output(): # disables prints from GoogleSearch
92
+ with io.capture_output(): # disables prints from GoogleSearch
88
93
  search = GoogleSearch(query)
89
94
  res = search.get_dict()
90
95
 
@@ -98,7 +103,7 @@ class SerpApiEngine(Engine):
98
103
  return output, metadata
99
104
 
100
105
  def prepare(self, argument):
101
- res = ''
106
+ res = ""
102
107
  res += str(argument.prop.query)
103
108
  res += str(argument.prop.processed_input)
104
109
  argument.prop.prepared_input = res
@@ -15,7 +15,7 @@ try:
15
15
  from whisper.audio import N_SAMPLES # @NOTE: sample_rate (16_000) * chunk_length (30) = 480_000
16
16
  from whisper.tokenizer import get_tokenizer
17
17
  except ImportError:
18
- whisper = None
18
+ whisper = None
19
19
  N_SAMPLES = 16_000 * 30
20
20
 
21
21
 
@@ -36,7 +36,9 @@ class WhisperTimestampsFormatter(Expression):
36
36
  start = prev_end
37
37
  prev_end = end
38
38
  prev_start = start
39
- result.append(f"{self._format_to_hours(start + (i*30))} {self._get_sentence(head)}")
39
+ result.append(
40
+ f"{self._format_to_hours(start + (i * 30))} {self._get_sentence(head)}"
41
+ )
40
42
  continue
41
43
  if start < prev_start:
42
44
  continue
@@ -47,7 +49,9 @@ class WhisperTimestampsFormatter(Expression):
47
49
  start += prev_end
48
50
  end = 30 if start + delta > 30 else start + delta
49
51
  prev_end = end
50
- result.append(f"{self._format_to_hours(start + (i*30))} {self._get_sentence(head)}")
52
+ result.append(
53
+ f"{self._format_to_hours(start + (i * 30))} {self._get_sentence(head)}"
54
+ )
51
55
  return "\n".join(result)
52
56
 
53
57
  def _filter_empty_string(self, s: str) -> list[str]:
@@ -88,7 +92,7 @@ class WhisperResult(Result):
88
92
  return result
89
93
 
90
94
  def _seconds(self, tmp: str) -> int:
91
- h, m ,s = tmp.split(":")
95
+ h, m, s = tmp.split(":")
92
96
  return int(h) * 3600 + int(m) * 60 + int(s)
93
97
 
94
98
 
@@ -96,37 +100,44 @@ class WhisperEngine(Engine):
96
100
  def __init__(self, model: str | None = None, to_device: str | None = None):
97
101
  super().__init__()
98
102
  self.config = SYMAI_CONFIG
99
- self.model = None # lazy loading
100
- self.model_id = self.config['SPEECH_TO_TEXT_ENGINE_MODEL'] if model is None else model
101
- self.old_model_id = self.config['SPEECH_TO_TEXT_ENGINE_MODEL'] if model is None else model
103
+ self.model = None # lazy loading
104
+ self.model_id = self.config["SPEECH_TO_TEXT_ENGINE_MODEL"] if model is None else model
105
+ self.old_model_id = self.config["SPEECH_TO_TEXT_ENGINE_MODEL"] if model is None else model
102
106
  self.tokens = []
103
107
  self.text = []
104
108
  self.formatter = WhisperTimestampsFormatter()
105
109
  self.name = self.__class__.__name__
106
110
  if self.model is None or self.model_id != self.old_model_id:
107
- device_fallback = 'cpu'
111
+ device_fallback = "cpu"
108
112
  device = "cuda" if torch.cuda.is_available() else device_fallback
109
- device = to_device if to_device is not None else device_fallback # user preference over auto detection
113
+ device = (
114
+ to_device if to_device is not None else device_fallback
115
+ ) # user preference over auto detection
110
116
  try:
111
117
  self.model = whisper.load_model(self.model_id, device=device)
112
118
  except RuntimeError:
113
- UserMessage(f"Whisper failed to load model on device {device}. Fallback to {device_fallback}.")
119
+ UserMessage(
120
+ f"Whisper failed to load model on device {device}. Fallback to {device_fallback}."
121
+ )
114
122
  self.model = whisper.load_model(self.model_id, device=device_fallback)
115
123
  self.old_model_id = self.model_id
116
124
 
117
125
  self._try_compile()
118
126
 
119
127
  def id(self) -> str:
120
- if self.config['SPEECH_TO_TEXT_ENGINE_MODEL']:
128
+ if self.config["SPEECH_TO_TEXT_ENGINE_MODEL"]:
121
129
  if whisper is None:
122
- UserMessage("Whisper is not installed. Please install it with `pip install symbolicai[whisper]`", raise_with=ImportError)
123
- return 'speech-to-text'
124
- return super().id() # default to unregistered
130
+ UserMessage(
131
+ "Whisper is not installed. Please install it with `pip install symbolicai[whisper]`",
132
+ raise_with=ImportError,
133
+ )
134
+ return "speech-to-text"
135
+ return super().id() # default to unregistered
125
136
 
126
137
  def command(self, *args, **kwargs):
127
138
  super().command(*args, **kwargs)
128
- if 'SPEECH_TO_TEXT_ENGINE_MODEL' in kwargs:
129
- self.model_id = kwargs['SPEECH_TO_TEXT_ENGINE_MODEL']
139
+ if "SPEECH_TO_TEXT_ENGINE_MODEL" in kwargs:
140
+ self.model_id = kwargs["SPEECH_TO_TEXT_ENGINE_MODEL"]
130
141
 
131
142
  def forward(self, argument):
132
143
  assert whisper is not None, "Whisper is not installed. Please install it first."
@@ -140,16 +151,17 @@ class WhisperEngine(Engine):
140
151
  without_timestamps = kwargs.get("without_timestamps", False)
141
152
 
142
153
  raw_result = []
143
- if prompt == 'detect_language':
144
- #@NOTE: the accuracy of mel spectrogram is not good enough; don't use it to transcribe
154
+ if prompt == "detect_language":
155
+ # @NOTE: the accuracy of mel spectrogram is not good enough; don't use it to transcribe
145
156
  audio = whisper.pad_or_trim(audio)
146
157
  mel = whisper.log_mel_spectrogram(audio).to(self.model.device)
147
158
  _, probs = self.model.detect_language(mel)
148
159
  rsp = max(probs, key=probs.get)
149
- elif prompt == 'decode':
160
+ elif prompt == "decode":
150
161
  if show_pbar:
151
162
  # Suppress tqdm warning; keep optional dependency lazy.
152
- from tqdm import tqdm # noqa
163
+ from tqdm import tqdm # noqa
164
+
153
165
  pbar = tqdm(self._get_chunks(audio))
154
166
  else:
155
167
  pbar = self._get_chunks(audio)
@@ -163,11 +175,9 @@ class WhisperEngine(Engine):
163
175
  )
164
176
  raw_result.append(result)
165
177
  self.text.append(result["text"])
166
- self.tokens.append([
167
- token
168
- for segment in result["segments"]
169
- for token in segment["tokens"]
170
- ])
178
+ self.tokens.append(
179
+ [token for segment in result["segments"] for token in segment["tokens"]]
180
+ )
171
181
  if without_timestamps is not None:
172
182
  tokenizer = get_tokenizer(self.model.is_multilingual)
173
183
  tokens = [tokenizer.decode_with_timestamps(t) for t in self.tokens]
@@ -186,7 +196,7 @@ class WhisperEngine(Engine):
186
196
  def prepare(self, argument):
187
197
  assert not argument.prop.processed_input, "Whisper does not support processed_input."
188
198
  assert argument.prop.audio, "Whisper requires audio input."
189
- audio_file = str(argument.prop.audio)
199
+ audio_file = str(argument.prop.audio)
190
200
  audio = whisper.load_audio(audio_file)
191
201
  argument.prop.prepared_input = (audio_file, audio)
192
202
 
@@ -196,7 +206,7 @@ class WhisperEngine(Engine):
196
206
  """
197
207
  size = len(it)
198
208
  for i in range(0, size, batch):
199
- yield torch.tensor(it[i:min(i + batch, size)]).to(self.model.device)
209
+ yield torch.tensor(it[i : min(i + batch, size)]).to(self.model.device)
200
210
 
201
211
  def _try_compile(self):
202
212
  with contextlib.suppress(Exception):
@@ -8,7 +8,10 @@ from ...settings import SYMAI_CONFIG
8
8
  try:
9
9
  import wolframalpha as wa
10
10
  except ImportError:
11
- UserMessage("WolframAlpha is not installed. Please install it with `pip install symbolicai[wolframalpha]`", raise_with=ImportError)
11
+ UserMessage(
12
+ "WolframAlpha is not installed. Please install it with `pip install symbolicai[wolframalpha]`",
13
+ raise_with=ImportError,
14
+ )
12
15
 
13
16
 
14
17
  class WolframResult(Result):
@@ -22,19 +25,19 @@ class WolframAlphaEngine(Engine):
22
25
  def __init__(self, api_key: str | None = None):
23
26
  super().__init__()
24
27
  self.config = deepcopy(SYMAI_CONFIG)
25
- self.api_key = self.config['SYMBOLIC_ENGINE_API_KEY'] if api_key is None else api_key
28
+ self.api_key = self.config["SYMBOLIC_ENGINE_API_KEY"] if api_key is None else api_key
26
29
  self.client = wa.Client(self.api_key)
27
30
  self.name = self.__class__.__name__
28
31
 
29
32
  def id(self) -> str:
30
- if self.config['SYMBOLIC_ENGINE_API_KEY']:
31
- return 'symbolic'
32
- return super().id() # default to unregistered
33
+ if self.config["SYMBOLIC_ENGINE_API_KEY"]:
34
+ return "symbolic"
35
+ return super().id() # default to unregistered
33
36
 
34
37
  def command(self, *args, **kwargs):
35
38
  super().command(*args, **kwargs)
36
- if 'SYMBOLIC_ENGINE_API_KEY' in kwargs:
37
- self.api_key = kwargs['SYMBOLIC_ENGINE_API_KEY']
39
+ if "SYMBOLIC_ENGINE_API_KEY" in kwargs:
40
+ self.api_key = kwargs["SYMBOLIC_ENGINE_API_KEY"]
38
41
  self.client = wa.Client(self.api_key)
39
42
 
40
43
  def forward(self, argument):
@@ -45,7 +48,10 @@ class WolframAlphaEngine(Engine):
45
48
  rsp = self.client.query(queries)
46
49
  rsp = WolframResult(rsp)
47
50
  except Exception as e:
48
- UserMessage(f'Failed to interact with WolframAlpha: {e}.\n\n If you are getting an error related to "assert", that is a well-known issue with WolframAlpha. There is a manual fix for this issue: https://github.com/jaraco/wolframalpha/pull/34/commits/6eb3828ee812f65592e00629710fc027d40e7bd1', raise_with=ValueError)
51
+ UserMessage(
52
+ f'Failed to interact with WolframAlpha: {e}.\n\n If you are getting an error related to "assert", that is a well-known issue with WolframAlpha. There is a manual fix for this issue: https://github.com/jaraco/wolframalpha/pull/34/commits/6eb3828ee812f65592e00629710fc027d40e7bd1',
53
+ raise_with=ValueError,
54
+ )
49
55
 
50
56
  metadata = {}
51
57
 
@@ -14,33 +14,29 @@ class TTSEngine(Engine):
14
14
  def __init__(self, api_key: str | None = None, model: str | None = None):
15
15
  super().__init__()
16
16
  self.config = SYMAI_CONFIG
17
- self.api_key = self.config['TEXT_TO_SPEECH_ENGINE_API_KEY'] if api_key is None else api_key
18
- self.model_id = self.config['TEXT_TO_SPEECH_ENGINE_MODEL'] if model is None else model
17
+ self.api_key = self.config["TEXT_TO_SPEECH_ENGINE_API_KEY"] if api_key is None else api_key
18
+ self.model_id = self.config["TEXT_TO_SPEECH_ENGINE_MODEL"] if model is None else model
19
19
  self.tokens = []
20
20
  self.text = []
21
21
  self.client = OpenAI(api_key=self.api_key)
22
22
  self.name = self.__class__.__name__
23
23
 
24
24
  def id(self) -> str:
25
- if self.config['TEXT_TO_SPEECH_ENGINE_API_KEY']:
26
- return 'text-to-speech'
27
- return super().id() # default to unregistered
25
+ if self.config["TEXT_TO_SPEECH_ENGINE_API_KEY"]:
26
+ return "text-to-speech"
27
+ return super().id() # default to unregistered
28
28
 
29
29
  def command(self, *args, **kwargs):
30
30
  super().command(*args, **kwargs)
31
- if 'TEXT_TO_SPEECH_ENGINE_API_KEY' in kwargs:
32
- self.api_key = kwargs['TEXT_TO_SPEECH_ENGINE_API_KEY']
33
- if 'TEXT_TO_SPEECH_ENGINE_MODEL' in kwargs:
34
- self.model_id = kwargs['TEXT_TO_SPEECH_ENGINE_MODEL']
31
+ if "TEXT_TO_SPEECH_ENGINE_API_KEY" in kwargs:
32
+ self.api_key = kwargs["TEXT_TO_SPEECH_ENGINE_API_KEY"]
33
+ if "TEXT_TO_SPEECH_ENGINE_MODEL" in kwargs:
34
+ self.model_id = kwargs["TEXT_TO_SPEECH_ENGINE_MODEL"]
35
35
 
36
36
  def forward(self, argument):
37
37
  voice, path, prompt = argument.prop.prepared_input
38
38
 
39
- rsp = self.client.audio.speech.create(
40
- model=self.model_id,
41
- voice=voice,
42
- input=prompt
43
- )
39
+ rsp = self.client.audio.speech.create(model=self.model_id, voice=voice, input=prompt)
44
40
 
45
41
  metadata = {}
46
42
 
@@ -51,9 +47,9 @@ class TTSEngine(Engine):
51
47
 
52
48
  def prepare(self, argument):
53
49
  assert not argument.prop.processed_input, "TTSEngine does not support processed_input."
54
- assert 'voice' in argument.kwargs, "TTS requires voice selection."
55
- assert 'path' in argument.kwargs, "TTS requires path selection."
56
- voice = str(argument.kwargs['voice']).lower()
57
- audio_file = str(argument.kwargs['path'])
58
- prompt = str(argument.prop.prompt)
50
+ assert "voice" in argument.kwargs, "TTS requires voice selection."
51
+ assert "path" in argument.kwargs, "TTS requires path selection."
52
+ voice = str(argument.kwargs["voice"]).lower()
53
+ audio_file = str(argument.kwargs["path"])
54
+ prompt = str(argument.prop.prompt)
59
55
  argument.prop.prepared_input = (voice, audio_file, prompt)
@@ -17,22 +17,22 @@ logging.getLogger("PIL").setLevel(logging.WARNING)
17
17
  class CLIPEngine(Engine):
18
18
  def __init__(self, model: str | None = None):
19
19
  super().__init__()
20
- self.model = None # lazy loading
21
- self.preprocessor = None # lazy loading
20
+ self.model = None # lazy loading
21
+ self.preprocessor = None # lazy loading
22
22
  self.config = SYMAI_CONFIG
23
- self.model_id = self.config['VISION_ENGINE_MODEL'] if model is None else model
24
- self.old_model_id = self.config['VISION_ENGINE_MODEL'] if model is None else model
23
+ self.model_id = self.config["VISION_ENGINE_MODEL"] if model is None else model
24
+ self.old_model_id = self.config["VISION_ENGINE_MODEL"] if model is None else model
25
25
  self.name = self.__class__.__name__
26
26
 
27
27
  def id(self) -> str:
28
- if self.config['VISION_ENGINE_MODEL']:
29
- return 'text_vision'
30
- return super().id() # default to unregistered
28
+ if self.config["VISION_ENGINE_MODEL"]:
29
+ return "text_vision"
30
+ return super().id() # default to unregistered
31
31
 
32
32
  def command(self, *args, **kwargs):
33
33
  super().command(*args, **kwargs)
34
- if 'VISION_ENGINE_MODEL' in kwargs:
35
- self.model_id = kwargs['VISION_ENGINE_MODEL']
34
+ if "VISION_ENGINE_MODEL" in kwargs:
35
+ self.model_id = kwargs["VISION_ENGINE_MODEL"]
36
36
 
37
37
  def load_images(self, image):
38
38
  images = []
@@ -43,35 +43,41 @@ class CLIPEngine(Engine):
43
43
  if isinstance(img, bytes):
44
44
  images.append(Image.open(BytesIO(img)))
45
45
  elif isinstance(img, str):
46
- image_source = requests.get(img, stream=True).raw if img.startswith('http') else img
46
+ image_source = requests.get(img, stream=True).raw if img.startswith("http") else img
47
47
  image = Image.open(image_source)
48
48
  images.append(image)
49
49
  return images
50
50
 
51
51
  def forward(self, argument):
52
- image_url, text = argument.prop.prepared_input
52
+ image_url, text = argument.prop.prepared_input
53
53
 
54
54
  if self.model is None or self.model_id != self.old_model_id:
55
- self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
56
- self.model = CLIPModel.from_pretrained(self.model_id).to(self.device)
57
- self.processor = CLIPProcessor.from_pretrained(self.model_id)
55
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
56
+ self.model = CLIPModel.from_pretrained(self.model_id).to(self.device)
57
+ self.processor = CLIPProcessor.from_pretrained(self.model_id)
58
58
  self.old_model_id = self.model_id
59
59
 
60
60
  if text is None and image_url is not None:
61
- image = self.load_images(image_url)
62
- inputs = self.processor(images=image, return_tensors="pt").to(self.device)
63
- rsp = self.model.get_image_features(**inputs)
61
+ image = self.load_images(image_url)
62
+ inputs = self.processor(images=image, return_tensors="pt").to(self.device)
63
+ rsp = self.model.get_image_features(**inputs)
64
64
  elif image_url is None and text is not None:
65
- inputs = self.processor(text=text, return_tensors="pt").to(self.device)
66
- rsp = self.model.get_text_features(**inputs)
65
+ inputs = self.processor(text=text, return_tensors="pt").to(self.device)
66
+ rsp = self.model.get_text_features(**inputs)
67
67
  elif image_url is not None and text is not None:
68
- image = self.load_images(image_url)
69
- inputs = self.processor(text=text, images=image, return_tensors="pt", padding=True).to(self.device)
70
- outputs = self.model(**inputs)
71
- logits_per_image = outputs.logits_per_image # this is the image-text similarity score
72
- rsp = logits_per_image.softmax(dim=1) # we can take the softmax to get the label probabilities
68
+ image = self.load_images(image_url)
69
+ inputs = self.processor(text=text, images=image, return_tensors="pt", padding=True).to(
70
+ self.device
71
+ )
72
+ outputs = self.model(**inputs)
73
+ logits_per_image = outputs.logits_per_image # this is the image-text similarity score
74
+ rsp = logits_per_image.softmax(
75
+ dim=1
76
+ ) # we can take the softmax to get the label probabilities
73
77
  else:
74
- UserMessage("CLIPEngine requires either image or text input.", raise_with=NotImplementedError)
78
+ UserMessage(
79
+ "CLIPEngine requires either image or text input.", raise_with=NotImplementedError
80
+ )
75
81
 
76
82
  rsp = rsp.squeeze().detach().cpu().numpy()
77
83
 
@@ -81,7 +87,7 @@ class CLIPEngine(Engine):
81
87
 
82
88
  def prepare(self, argument):
83
89
  assert not argument.prop.processed_input, "CLIPEngine does not support processed_input."
84
- kwargs = argument.kwargs
85
- image_url = argument.kwargs['image'] if 'image' in kwargs else None
86
- text = argument.kwargs['text'] if 'text' in kwargs else None
90
+ kwargs = argument.kwargs
91
+ image_url = argument.kwargs["image"] if "image" in kwargs else None
92
+ text = argument.kwargs["text"] if "text" in kwargs else None
87
93
  argument.prop.prepared_input = (image_url, text)
@@ -1,4 +1,3 @@
1
-
2
1
  from ....utils import UserMessage
3
2
  from ...base import Engine
4
3
 
@@ -9,14 +8,14 @@ class UserInputEngine(Engine):
9
8
  self.name = self.__class__.__name__
10
9
 
11
10
  def id(self) -> str:
12
- return 'userinput'
11
+ return "userinput"
13
12
 
14
13
  def forward(self, argument):
15
14
  msg = argument.prop.prepared_input
16
15
  kwargs = argument.kwargs
17
16
 
18
- mock = kwargs.get('mock', False)
19
- if mock: # mock user input
17
+ mock = kwargs.get("mock", False)
18
+ if mock: # mock user input
20
19
  UserMessage(msg)
21
20
  rsp = mock
22
21
  else:
@@ -1,5 +1,7 @@
1
1
  from .anthropic import SUPPORTED_CHAT_MODELS as ANTHROPIC_CHAT_MODELS
2
2
  from .anthropic import SUPPORTED_REASONING_MODELS as ANTHROPIC_REASONING_MODELS
3
+ from .cerebras import SUPPORTED_CHAT_MODELS as CEREBRAS_CHAT_MODELS
4
+ from .cerebras import SUPPORTED_REASONING_MODELS as CEREBRAS_REASONING_MODELS
3
5
  from .deepseek import SUPPORTED_CHAT_MODELS as DEEPSEEK_CHAT_MODELS
4
6
  from .deepseek import SUPPORTED_REASONING_MODELS as DEEPSEEK_REASONING_MODELS
5
7
  from .google import SUPPORTED_CHAT_MODELS as GOOGLE_CHAT_MODELS
@@ -12,6 +14,8 @@ from .openai import SUPPORTED_REASONING_MODELS as OPENAI_REASONING_MODELS
12
14
  __all__ = [
13
15
  "ANTHROPIC_CHAT_MODELS",
14
16
  "ANTHROPIC_REASONING_MODELS",
17
+ "CEREBRAS_CHAT_MODELS",
18
+ "CEREBRAS_REASONING_MODELS",
15
19
  "DEEPSEEK_CHAT_MODELS",
16
20
  "DEEPSEEK_REASONING_MODELS",
17
21
  "GOOGLE_CHAT_MODELS",