symbolicai 0.21.0__py3-none-any.whl → 1.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. symai/__init__.py +269 -173
  2. symai/backend/base.py +123 -110
  3. symai/backend/engines/drawing/engine_bfl.py +45 -44
  4. symai/backend/engines/drawing/engine_gpt_image.py +112 -97
  5. symai/backend/engines/embedding/engine_llama_cpp.py +63 -52
  6. symai/backend/engines/embedding/engine_openai.py +25 -21
  7. symai/backend/engines/execute/engine_python.py +19 -18
  8. symai/backend/engines/files/engine_io.py +104 -95
  9. symai/backend/engines/imagecaptioning/engine_blip2.py +28 -24
  10. symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +102 -79
  11. symai/backend/engines/index/engine_pinecone.py +124 -97
  12. symai/backend/engines/index/engine_qdrant.py +1011 -0
  13. symai/backend/engines/index/engine_vectordb.py +84 -56
  14. symai/backend/engines/lean/engine_lean4.py +96 -52
  15. symai/backend/engines/neurosymbolic/__init__.py +41 -13
  16. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +330 -248
  17. symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +329 -264
  18. symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
  19. symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +118 -88
  20. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +344 -299
  21. symai/backend/engines/neurosymbolic/engine_groq.py +173 -115
  22. symai/backend/engines/neurosymbolic/engine_huggingface.py +114 -84
  23. symai/backend/engines/neurosymbolic/engine_llama_cpp.py +144 -118
  24. symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +415 -307
  25. symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +394 -231
  26. symai/backend/engines/ocr/engine_apilayer.py +23 -27
  27. symai/backend/engines/output/engine_stdout.py +10 -13
  28. symai/backend/engines/{webscraping → scrape}/engine_requests.py +101 -54
  29. symai/backend/engines/search/engine_openai.py +100 -88
  30. symai/backend/engines/search/engine_parallel.py +665 -0
  31. symai/backend/engines/search/engine_perplexity.py +44 -45
  32. symai/backend/engines/search/engine_serpapi.py +37 -34
  33. symai/backend/engines/speech_to_text/engine_local_whisper.py +54 -51
  34. symai/backend/engines/symbolic/engine_wolframalpha.py +15 -9
  35. symai/backend/engines/text_to_speech/engine_openai.py +20 -26
  36. symai/backend/engines/text_vision/engine_clip.py +39 -37
  37. symai/backend/engines/userinput/engine_console.py +5 -6
  38. symai/backend/mixin/__init__.py +13 -0
  39. symai/backend/mixin/anthropic.py +48 -38
  40. symai/backend/mixin/deepseek.py +6 -5
  41. symai/backend/mixin/google.py +7 -4
  42. symai/backend/mixin/groq.py +2 -4
  43. symai/backend/mixin/openai.py +140 -110
  44. symai/backend/settings.py +87 -20
  45. symai/chat.py +216 -123
  46. symai/collect/__init__.py +7 -1
  47. symai/collect/dynamic.py +80 -70
  48. symai/collect/pipeline.py +67 -51
  49. symai/collect/stats.py +161 -109
  50. symai/components.py +707 -360
  51. symai/constraints.py +24 -12
  52. symai/core.py +1857 -1233
  53. symai/core_ext.py +83 -80
  54. symai/endpoints/api.py +166 -104
  55. symai/extended/.DS_Store +0 -0
  56. symai/extended/__init__.py +46 -12
  57. symai/extended/api_builder.py +29 -21
  58. symai/extended/arxiv_pdf_parser.py +23 -14
  59. symai/extended/bibtex_parser.py +9 -6
  60. symai/extended/conversation.py +156 -126
  61. symai/extended/document.py +50 -30
  62. symai/extended/file_merger.py +57 -14
  63. symai/extended/graph.py +51 -32
  64. symai/extended/html_style_template.py +18 -14
  65. symai/extended/interfaces/blip_2.py +2 -3
  66. symai/extended/interfaces/clip.py +4 -3
  67. symai/extended/interfaces/console.py +9 -1
  68. symai/extended/interfaces/dall_e.py +4 -2
  69. symai/extended/interfaces/file.py +2 -0
  70. symai/extended/interfaces/flux.py +4 -2
  71. symai/extended/interfaces/gpt_image.py +16 -7
  72. symai/extended/interfaces/input.py +2 -1
  73. symai/extended/interfaces/llava.py +1 -2
  74. symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +4 -3
  75. symai/extended/interfaces/naive_vectordb.py +9 -10
  76. symai/extended/interfaces/ocr.py +5 -3
  77. symai/extended/interfaces/openai_search.py +2 -0
  78. symai/extended/interfaces/parallel.py +30 -0
  79. symai/extended/interfaces/perplexity.py +2 -0
  80. symai/extended/interfaces/pinecone.py +12 -9
  81. symai/extended/interfaces/python.py +2 -0
  82. symai/extended/interfaces/serpapi.py +3 -1
  83. symai/extended/interfaces/terminal.py +2 -4
  84. symai/extended/interfaces/tts.py +3 -2
  85. symai/extended/interfaces/whisper.py +3 -2
  86. symai/extended/interfaces/wolframalpha.py +2 -1
  87. symai/extended/metrics/__init__.py +11 -1
  88. symai/extended/metrics/similarity.py +14 -13
  89. symai/extended/os_command.py +39 -29
  90. symai/extended/packages/__init__.py +29 -3
  91. symai/extended/packages/symdev.py +51 -43
  92. symai/extended/packages/sympkg.py +41 -35
  93. symai/extended/packages/symrun.py +63 -50
  94. symai/extended/repo_cloner.py +14 -12
  95. symai/extended/seo_query_optimizer.py +15 -13
  96. symai/extended/solver.py +116 -91
  97. symai/extended/summarizer.py +12 -10
  98. symai/extended/taypan_interpreter.py +17 -18
  99. symai/extended/vectordb.py +122 -92
  100. symai/formatter/__init__.py +9 -1
  101. symai/formatter/formatter.py +51 -47
  102. symai/formatter/regex.py +70 -69
  103. symai/functional.py +325 -176
  104. symai/imports.py +190 -147
  105. symai/interfaces.py +57 -28
  106. symai/memory.py +45 -35
  107. symai/menu/screen.py +28 -19
  108. symai/misc/console.py +66 -56
  109. symai/misc/loader.py +8 -5
  110. symai/models/__init__.py +17 -1
  111. symai/models/base.py +395 -236
  112. symai/models/errors.py +1 -2
  113. symai/ops/__init__.py +32 -22
  114. symai/ops/measures.py +24 -25
  115. symai/ops/primitives.py +1149 -731
  116. symai/post_processors.py +58 -50
  117. symai/pre_processors.py +86 -82
  118. symai/processor.py +21 -13
  119. symai/prompts.py +764 -685
  120. symai/server/huggingface_server.py +135 -49
  121. symai/server/llama_cpp_server.py +21 -11
  122. symai/server/qdrant_server.py +206 -0
  123. symai/shell.py +100 -42
  124. symai/shellsv.py +700 -492
  125. symai/strategy.py +630 -346
  126. symai/symbol.py +368 -322
  127. symai/utils.py +100 -78
  128. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +22 -10
  129. symbolicai-1.1.0.dist-info/RECORD +168 -0
  130. symbolicai-0.21.0.dist-info/RECORD +0 -162
  131. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
  132. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
  133. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
  134. {symbolicai-0.21.0.dist-info → symbolicai-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,14 @@
1
1
  import base64
2
+ import contextlib
2
3
  import logging
3
4
  import tempfile
4
5
  from pathlib import Path
5
- from typing import Optional
6
6
 
7
7
  import openai
8
8
  import requests
9
9
 
10
10
  from ....symbol import Result
11
+ from ....utils import UserMessage
11
12
  from ...base import Engine
12
13
  from ...settings import SYMAI_CONFIG
13
14
 
@@ -25,21 +26,23 @@ class GPTImageResult(Result):
25
26
  Exposes .value as the raw response and ._value as the
26
27
  first URL or decoded b64 image string.
27
28
  """
29
+
28
30
  def __init__(self, value, **kwargs):
29
31
  super().__init__(value, **kwargs)
30
32
  imgs = []
31
33
  for item in value.data:
32
34
  has_url = hasattr(item, "url")
33
35
  has_b64 = hasattr(item, "b64_json")
34
- path = tempfile.NamedTemporaryFile(suffix=".png", delete=False).name
36
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
37
+ path = tmp_file.name
35
38
  if has_url and item.url is not None:
36
39
  request = requests.get(item.url, allow_redirects=True)
37
40
  request.raise_for_status()
38
- with open(path, "wb") as f:
41
+ with Path(path).open("wb") as f:
39
42
  f.write(request.content)
40
43
  elif has_b64 and item.b64_json is not None:
41
44
  raw = base64.b64decode(item.b64_json)
42
- with open(path, "wb") as f:
45
+ with Path(path).open("wb") as f:
43
46
  f.write(raw)
44
47
  imgs.append(path)
45
48
  self._value = imgs
@@ -47,28 +50,21 @@ class GPTImageResult(Result):
47
50
 
48
51
  class GPTImageEngine(Engine):
49
52
  """
50
- A dropin engine for OpenAI's unified Images API,
53
+ A drop-in engine for OpenAI's unified Images API,
51
54
  supporting gpt-image-1, dall-e-2, dall-e-3,
52
55
  with all the extra parameters (background, moderation, etc).
53
56
  """
57
+
54
58
  def __init__(
55
59
  self,
56
- api_key: Optional[str] = None,
57
- model: Optional[str] = None,
60
+ api_key: str | None = None,
61
+ model: str | None = None,
58
62
  ):
59
63
  super().__init__()
60
64
  self.config = SYMAI_CONFIG
61
65
  # pick up a separate config slot if you like, or fall back
62
- openai.api_key = (
63
- self.config.get("DRAWING_ENGINE_API_KEY")
64
- if api_key is None
65
- else api_key
66
- )
67
- self.model = (
68
- self.config.get("DRAWING_ENGINE_MODEL")
69
- if model is None
70
- else model
71
- )
66
+ openai.api_key = self.config.get("DRAWING_ENGINE_API_KEY") if api_key is None else api_key
67
+ self.model = self.config.get("DRAWING_ENGINE_MODEL") if model is None else model
72
68
  self.name = self.__class__.__name__
73
69
  # quiet OpenAI's internal logger
74
70
  log = logging.getLogger("openai")
@@ -83,7 +79,7 @@ class GPTImageEngine(Engine):
83
79
 
84
80
  def command(self, *args, **kwargs):
85
81
  """
86
- Allow hotswapping API key or model at runtime.
82
+ Allow hot-swapping API key or model at runtime.
87
83
  """
88
84
  super().command(*args, **kwargs)
89
85
  if "DRAWING_ENGINE_API_KEY" in kwargs:
@@ -105,98 +101,117 @@ class GPTImageEngine(Engine):
105
101
  operation = kwargs.get("operation")
106
102
 
107
103
  if operation is None:
108
- raise ValueError("Operation not specified!")
104
+ UserMessage("Operation not specified!", raise_with=ValueError)
109
105
 
110
106
  n = kwargs.get("n", 1)
111
107
 
112
- if "size" in kwargs:
113
- if isinstance(kwargs["size"], int):
114
- s = kwargs["size"]
115
- kwargs["size"] = f"{s}x{s}"
108
+ self._normalize_size(kwargs)
116
109
 
117
110
  except_remedy = kwargs.get("except_remedy", None)
118
111
 
119
112
  callback = None
120
113
  try:
121
- if operation == "create":
122
- create_kwargs = {
123
- "model": model,
124
- "prompt": prompt,
125
- "n": n,
126
- "size": kwargs.get("size"),
127
- }
128
-
129
- if model == "dall-e-3":
130
- create_kwargs["response_format"] = kwargs.get("response_format", "url")
131
- create_kwargs["quality"] = kwargs.get("quality", "standard")
132
- create_kwargs["style"] = kwargs.get("style", "vivid")
133
-
134
- if model.startswith("gpt-image-"):
135
- create_kwargs["quality"] = kwargs.get("quality", "medium")
136
- create_kwargs["moderation"] = kwargs.get("moderation", "auto")
137
- create_kwargs["background"] = kwargs.get("background", "auto")
138
- create_kwargs["output_format"] = kwargs.get("output_compression", "png")
139
- if create_kwargs["output_format"] == "jpeg" or create_kwargs["output_format"] == "webp":
140
- create_kwargs["output_compression"] = kwargs.get("output_compression", "100")
141
-
142
- callback = openai.images.generate
143
- res = openai.images.generate(**create_kwargs)
144
-
145
- elif operation == "variation":
146
- assert "image_path" in kwargs, "image_path required for variation"
147
- callback = openai.images.create_variation
148
- with open(kwargs["image_path"], "rb") as img:
149
- res = openai.images.create_variation(
150
- model=model,
151
- image=img,
152
- n=n,
153
- size=kwargs.get("size"),
154
- response_format=kwargs.get("response_format", "url"),
155
- )
156
-
157
- elif operation == "edit":
158
- assert "image_path" in kwargs, "image_path required for edit"
159
- # allow either a single path or a list of paths
160
- img_paths = kwargs["image_path"]
161
- if not isinstance(img_paths, (list, tuple)):
162
- img_paths = [img_paths]
163
- # open all images
164
- image_files = [open(p, "rb") for p in img_paths]
165
- # optional mask (only for the first image)
166
- mask_file = None
167
- if "mask_path" in kwargs and kwargs["mask_path"] is not None:
168
- mask_file = open(kwargs["mask_path"], "rb")
169
- # construct API args
170
- edit_kwargs = {
171
- "model": model,
172
- "image": image_files if len(image_files) > 1 else image_files[0],
173
- "prompt": prompt,
174
- "n": n,
175
- "size": kwargs.get("size"),
176
- }
177
-
178
- if model.startswith("gpt-image-"):
179
- edit_kwargs["quality"] = kwargs.get("quality", "auto")
180
-
181
- if mask_file:
182
- edit_kwargs["mask"] = mask_file
183
- callback = openai.images.edit
184
-
185
- res = openai.images.edit(**edit_kwargs)
186
- # clean up file handles
187
- for f in image_files:
188
- f.close()
189
- if mask_file:
190
- mask_file.close()
191
- else:
192
- raise ValueError(f"Unknown image operation: {operation}")
193
-
114
+ callback = self._resolve_callback(operation)
115
+ callback, res = self._dispatch_operation(
116
+ operation=operation,
117
+ prompt=prompt,
118
+ model=model,
119
+ n=n,
120
+ kwargs=kwargs,
121
+ )
194
122
  except Exception as e:
195
123
  if except_remedy is None:
196
124
  raise
197
125
  res = except_remedy(self, e, callback, argument)
198
126
 
199
- # wrap it up
200
127
  metadata = {}
201
128
  result = GPTImageResult(res)
202
129
  return [result], metadata
130
+
131
+ def _normalize_size(self, kwargs):
132
+ if "size" in kwargs and isinstance(kwargs["size"], int):
133
+ s = kwargs["size"]
134
+ kwargs["size"] = f"{s}x{s}"
135
+
136
+ def _resolve_callback(self, operation):
137
+ if operation == "create":
138
+ return openai.images.generate
139
+ if operation == "variation":
140
+ return openai.images.create_variation
141
+ if operation == "edit":
142
+ return openai.images.edit
143
+ UserMessage(f"Unknown image operation: {operation}", raise_with=ValueError)
144
+ return openai.images.generate
145
+
146
+ def _dispatch_operation(self, operation, prompt, model, n, kwargs):
147
+ if operation == "create":
148
+ return self._execute_create(prompt, model, n, kwargs)
149
+ if operation == "variation":
150
+ return self._execute_variation(model, n, kwargs)
151
+ if operation == "edit":
152
+ return self._execute_edit(prompt, model, n, kwargs)
153
+ return UserMessage(f"Unknown image operation: {operation}", raise_with=ValueError)
154
+
155
+ def _execute_create(self, prompt, model, n, kwargs):
156
+ create_kwargs = {
157
+ "model": model,
158
+ "prompt": prompt,
159
+ "n": n,
160
+ "size": kwargs.get("size"),
161
+ }
162
+
163
+ if model == "dall-e-3":
164
+ create_kwargs["response_format"] = kwargs.get("response_format", "url")
165
+ create_kwargs["quality"] = kwargs.get("quality", "standard")
166
+ create_kwargs["style"] = kwargs.get("style", "vivid")
167
+
168
+ if model.startswith("gpt-image-"):
169
+ create_kwargs["quality"] = kwargs.get("quality", "medium")
170
+ create_kwargs["moderation"] = kwargs.get("moderation", "auto")
171
+ create_kwargs["background"] = kwargs.get("background", "auto")
172
+ create_kwargs["output_format"] = kwargs.get("output_compression", "png")
173
+ if create_kwargs["output_format"] == "jpeg" or create_kwargs["output_format"] == "webp":
174
+ create_kwargs["output_compression"] = kwargs.get("output_compression", "100")
175
+
176
+ callback = openai.images.generate
177
+ return callback, callback(**create_kwargs)
178
+
179
+ def _execute_variation(self, model, n, kwargs):
180
+ assert "image_path" in kwargs, "image_path required for variation"
181
+ callback = openai.images.create_variation
182
+ with Path(kwargs["image_path"]).open("rb") as img:
183
+ result = callback(
184
+ model=model,
185
+ image=img,
186
+ n=n,
187
+ size=kwargs.get("size"),
188
+ response_format=kwargs.get("response_format", "url"),
189
+ )
190
+ return callback, result
191
+
192
+ def _execute_edit(self, prompt, model, n, kwargs):
193
+ assert "image_path" in kwargs, "image_path required for edit"
194
+ img_paths = kwargs["image_path"]
195
+ if not isinstance(img_paths, (list, tuple)):
196
+ img_paths = [img_paths]
197
+ with contextlib.ExitStack() as stack:
198
+ image_files = [stack.enter_context(Path(p).open("rb")) for p in img_paths]
199
+ mask_file = None
200
+ if "mask_path" in kwargs and kwargs["mask_path"] is not None:
201
+ mask_file = stack.enter_context(Path(kwargs["mask_path"]).open("rb"))
202
+ edit_kwargs = {
203
+ "model": model,
204
+ "image": image_files if len(image_files) > 1 else image_files[0],
205
+ "prompt": prompt,
206
+ "n": n,
207
+ "size": kwargs.get("size"),
208
+ }
209
+
210
+ if model.startswith("gpt-image-"):
211
+ edit_kwargs["quality"] = kwargs.get("quality", "auto")
212
+
213
+ if mask_file:
214
+ edit_kwargs["mask"] = mask_file
215
+ callback = openai.images.edit
216
+ result = callback(**edit_kwargs)
217
+ return callback, result
@@ -1,14 +1,12 @@
1
1
  import asyncio
2
2
  import logging
3
- from multiprocessing import Value
4
- from typing import Optional
3
+ from typing import Any, ClassVar
5
4
 
6
5
  import aiohttp
7
6
  import nest_asyncio
8
- import numpy as np
9
7
 
10
8
  from ....core_ext import retry
11
- from ....utils import CustomUserWarning
9
+ from ....utils import UserMessage
12
10
  from ...base import Engine
13
11
  from ...settings import SYMAI_CONFIG, SYMSERVER_CONFIG
14
12
 
@@ -17,58 +15,66 @@ logging.getLogger("urllib").setLevel(logging.ERROR)
17
15
  logging.getLogger("httpx").setLevel(logging.ERROR)
18
16
  logging.getLogger("httpcore").setLevel(logging.ERROR)
19
17
 
18
+
20
19
  class LlamaCppEmbeddingEngine(Engine):
21
- _retry_params = {
22
- 'tries': 5,
23
- 'delay': 2,
24
- 'max_delay': 60,
25
- 'backoff': 2,
26
- 'jitter': (1, 5),
27
- 'graceful': True
20
+ _retry_params: ClassVar[dict[str, Any]] = {
21
+ "tries": 5,
22
+ "delay": 2,
23
+ "max_delay": 60,
24
+ "backoff": 2,
25
+ "jitter": (1, 5),
26
+ "graceful": True,
28
27
  }
29
- _timeout_params = {
30
- 'read': None,
31
- 'connect': None,
28
+ _timeout_params: ClassVar[dict[str, Any]] = {
29
+ "read": None,
30
+ "connect": None,
32
31
  }
33
32
 
34
- def __init__(
35
- self,
36
- retry_params: dict = _retry_params,
37
- timeout_params: dict = _timeout_params
38
- ):
33
+ def __init__(self, retry_params: dict = _retry_params, timeout_params: dict = _timeout_params):
39
34
  super().__init__()
40
35
  self.config = SYMAI_CONFIG
41
- if self.id() != 'embedding':
36
+ if self.id() != "embedding":
42
37
  return
43
- if not SYMSERVER_CONFIG.get('online'):
44
- CustomUserWarning('You are using the llama.cpp embedding engine, but the server endpoint is not started. Please start the server with `symserver [--args]`.', raise_with=ValueError)
38
+ if not SYMSERVER_CONFIG.get("online"):
39
+ UserMessage(
40
+ "You are using the llama.cpp embedding engine, but the server endpoint is not started. Please start the server with `symserver [--args]`.",
41
+ raise_with=ValueError,
42
+ )
45
43
 
46
- self.server_endpoint = f"http://{SYMSERVER_CONFIG.get('--host')}:{SYMSERVER_CONFIG.get('--port')}"
44
+ self.server_endpoint = (
45
+ f"http://{SYMSERVER_CONFIG.get('--host')}:{SYMSERVER_CONFIG.get('--port')}"
46
+ )
47
47
  self.timeout_params = self._validate_timeout_params(timeout_params)
48
48
  self.retry_params = self._validate_retry_params(retry_params)
49
49
  self.name = self.__class__.__name__
50
50
 
51
51
  def id(self) -> str:
52
- if self.config.get('EMBEDDING_ENGINE_MODEL') and self.config.get('EMBEDDING_ENGINE_MODEL').startswith('llama'):
53
- return 'embedding'
52
+ if self.config.get("EMBEDDING_ENGINE_MODEL") and self.config.get(
53
+ "EMBEDDING_ENGINE_MODEL"
54
+ ).startswith("llama"):
55
+ return "embedding"
54
56
  return super().id() # default to unregistered
55
57
 
56
58
  def command(self, *args, **kwargs):
57
59
  super().command(*args, **kwargs)
58
- if 'EMBEDDING_ENGINE_MODEL' in kwargs:
59
- self.model = kwargs['EMBEDDING_ENGINE_MODEL']
60
+ if "EMBEDDING_ENGINE_MODEL" in kwargs:
61
+ self.model = kwargs["EMBEDDING_ENGINE_MODEL"]
60
62
 
61
63
  def _validate_timeout_params(self, timeout_params):
62
64
  if not isinstance(timeout_params, dict):
63
- raise ValueError("timeout_params must be a dictionary")
64
- assert all(key in timeout_params for key in ['read', 'connect']), "Available keys: ['read', 'connect']"
65
+ UserMessage("timeout_params must be a dictionary", raise_with=ValueError)
66
+ assert all(key in timeout_params for key in ["read", "connect"]), (
67
+ "Available keys: ['read', 'connect']"
68
+ )
65
69
  return timeout_params
66
70
 
67
71
  def _validate_retry_params(self, retry_params):
68
72
  if not isinstance(retry_params, dict):
69
- raise ValueError("retry_params must be a dictionary")
70
- assert all(key in retry_params for key in ['tries', 'delay', 'max_delay', 'backoff', 'jitter', 'graceful']), \
71
- "Available keys: ['tries', 'delay', 'max_delay', 'backoff', 'jitter', 'graceful']"
73
+ UserMessage("retry_params must be a dictionary", raise_with=ValueError)
74
+ assert all(
75
+ key in retry_params
76
+ for key in ["tries", "delay", "max_delay", "backoff", "jitter", "graceful"]
77
+ ), "Available keys: ['tries', 'delay', 'max_delay', 'backoff', 'jitter', 'graceful']"
72
78
  return retry_params
73
79
 
74
80
  @staticmethod
@@ -77,7 +83,9 @@ class LlamaCppEmbeddingEngine(Engine):
77
83
  try:
78
84
  current_loop = asyncio.get_event_loop()
79
85
  if current_loop.is_closed():
80
- raise RuntimeError("Event loop is closed.")
86
+ msg = "Event loop is closed."
87
+ UserMessage(msg)
88
+ raise RuntimeError(msg)
81
89
  return current_loop
82
90
  except RuntimeError:
83
91
  new_loop = asyncio.new_event_loop()
@@ -86,20 +94,24 @@ class LlamaCppEmbeddingEngine(Engine):
86
94
 
87
95
  async def _arequest(self, text: str, embd_normalize: str) -> dict:
88
96
  """Makes an async HTTP request to the llama.cpp server."""
97
+
89
98
  @retry(**self.retry_params)
90
99
  async def _make_request():
91
100
  timeout = aiohttp.ClientTimeout(
92
- sock_connect=self.timeout_params['connect'],
93
- sock_read=self.timeout_params['read']
101
+ sock_connect=self.timeout_params["connect"], sock_read=self.timeout_params["read"]
94
102
  )
95
- async with aiohttp.ClientSession(timeout=timeout) as session:
96
- async with session.post(
103
+ async with (
104
+ aiohttp.ClientSession(timeout=timeout) as session,
105
+ session.post(
97
106
  f"{self.server_endpoint}/v1/embeddings",
98
- json={"content": text, "embd_normalize": embd_normalize}
99
- ) as res:
100
- if res.status != 200:
101
- raise ValueError(f"Request failed with status code: {res.status}")
102
- return await res.json()
107
+ json={"content": text, "embd_normalize": embd_normalize},
108
+ ) as res,
109
+ ):
110
+ if res.status != 200:
111
+ UserMessage(
112
+ f"Request failed with status code: {res.status}", raise_with=ValueError
113
+ )
114
+ return await res.json()
103
115
 
104
116
  return await _make_request()
105
117
 
@@ -108,11 +120,11 @@ class LlamaCppEmbeddingEngine(Engine):
108
120
  kwargs = argument.kwargs
109
121
 
110
122
  inp = prepared_input if isinstance(prepared_input, list) else [prepared_input]
111
- embd_normalize = kwargs.get('embd_normalize', -1) # -1 = no normalization
123
+ embd_normalize = kwargs.get("embd_normalize", -1) # -1 = no normalization
112
124
 
113
- new_dim = kwargs.get('new_dim')
125
+ new_dim = kwargs.get("new_dim")
114
126
  if new_dim:
115
- raise NotImplementedError("new_dim is not yet supported")
127
+ UserMessage("new_dim is not yet supported", raise_with=NotImplementedError)
116
128
 
117
129
  nest_asyncio.apply()
118
130
  loop = self._get_event_loop()
@@ -120,16 +132,15 @@ class LlamaCppEmbeddingEngine(Engine):
120
132
  try:
121
133
  res = loop.run_until_complete(self._arequest(inp, embd_normalize))
122
134
  except Exception as e:
123
- raise ValueError(f"Request failed with error: {str(e)}")
135
+ UserMessage(f"Request failed with error: {e!s}", raise_with=ValueError)
124
136
 
125
- if res is not None:
126
- output = [r["embedding"] for r in res] # B x 1 x D
127
- else:
128
- output = None
129
- metadata = {'raw_output': res}
137
+ output = [r["embedding"] for r in res] if res is not None else None # B x 1 x D
138
+ metadata = {"raw_output": res}
130
139
 
131
140
  return [output], metadata
132
141
 
133
142
  def prepare(self, argument):
134
- assert not argument.prop.processed_input, "LlamaCppEmbeddingEngine does not support processed_input."
143
+ assert not argument.prop.processed_input, (
144
+ "LlamaCppEmbeddingEngine does not support processed_input."
145
+ )
135
146
  argument.prop.prepared_input = argument.prop.entries
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from typing import Optional
3
2
 
4
3
  import numpy as np
5
4
  import openai
@@ -16,30 +15,32 @@ logging.getLogger("httpcore").setLevel(logging.ERROR)
16
15
 
17
16
 
18
17
  class EmbeddingEngine(Engine, OpenAIMixin):
19
- def __init__(self, api_key: Optional[str] = None, model: Optional[str] = None):
18
+ def __init__(self, api_key: str | None = None, model: str | None = None):
20
19
  super().__init__()
21
- logger = logging.getLogger('openai')
20
+ logger = logging.getLogger("openai")
22
21
  logger.setLevel(logging.WARNING)
23
22
  self.config = SYMAI_CONFIG
24
- if self.id() != 'embedding':
25
- return # do not initialize if not embedding; avoids conflict with llama.cpp check in EngineRepository.register_from_package
26
- openai.api_key = self.config['EMBEDDING_ENGINE_API_KEY'] if api_key is None else api_key
27
- self.model = self.config['EMBEDDING_ENGINE_MODEL'] if model is None else model
23
+ if self.id() != "embedding":
24
+ return # do not initialize if not embedding; avoids conflict with llama.cpp check in EngineRepository.register_from_package
25
+ openai.api_key = self.config["EMBEDDING_ENGINE_API_KEY"] if api_key is None else api_key
26
+ self.model = self.config["EMBEDDING_ENGINE_MODEL"] if model is None else model
28
27
  self.max_tokens = self.api_max_context_tokens()
29
28
  self.embedding_dim = self.api_embedding_dims()
30
29
  self.name = self.__class__.__name__
31
30
 
32
31
  def id(self) -> str:
33
- if self.config.get('EMBEDDING_ENGINE_API_KEY') and self.config['EMBEDDING_ENGINE_MODEL'].startswith('text-embedding'):
34
- return 'embedding'
35
- return super().id() # default to unregistered
32
+ if self.config.get("EMBEDDING_ENGINE_API_KEY") and self.config[
33
+ "EMBEDDING_ENGINE_MODEL"
34
+ ].startswith("text-embedding"):
35
+ return "embedding"
36
+ return super().id() # default to unregistered
36
37
 
37
38
  def command(self, *args, **kwargs):
38
39
  super().command(*args, **kwargs)
39
- if 'EMBEDDING_ENGINE_API_KEY' in kwargs:
40
- openai.api_key = kwargs['EMBEDDING_ENGINE_API_KEY']
41
- if 'EMBEDDING_ENGINE_MODEL' in kwargs:
42
- self.model = kwargs['EMBEDDING_ENGINE_MODEL']
40
+ if "EMBEDDING_ENGINE_API_KEY" in kwargs:
41
+ openai.api_key = kwargs["EMBEDDING_ENGINE_API_KEY"]
42
+ if "EMBEDDING_ENGINE_MODEL" in kwargs:
43
+ self.model = kwargs["EMBEDDING_ENGINE_MODEL"]
43
44
 
44
45
  def forward(self, argument):
45
46
  prepared_input = argument.prop.prepared_input
@@ -47,8 +48,8 @@ class EmbeddingEngine(Engine, OpenAIMixin):
47
48
  kwargs = argument.kwargs
48
49
 
49
50
  inp = prepared_input if isinstance(prepared_input, list) else [prepared_input]
50
- except_remedy = kwargs.get('except_remedy')
51
- new_dim = kwargs.get('new_dim')
51
+ except_remedy = kwargs.get("except_remedy")
52
+ new_dim = kwargs.get("new_dim")
52
53
 
53
54
  try:
54
55
  res = openai.embeddings.create(model=self.model, input=inp)
@@ -59,7 +60,9 @@ class EmbeddingEngine(Engine, OpenAIMixin):
59
60
  res = except_remedy(e, inp, callback, self, *args, **kwargs)
60
61
 
61
62
  if new_dim:
62
- mn = min(new_dim, self.embedding_dim) #@NOTE: new_dim should be less than or equal to the original embedding dim
63
+ mn = min(
64
+ new_dim, self.embedding_dim
65
+ ) # @NOTE: new_dim should be less than or equal to the original embedding dim
63
66
  output = [self._normalize_l2(r.embedding[:mn]) for r in res.data]
64
67
  else:
65
68
  output = [r.embedding for r in res.data]
@@ -69,7 +72,9 @@ class EmbeddingEngine(Engine, OpenAIMixin):
69
72
  return [output], metadata
70
73
 
71
74
  def prepare(self, argument):
72
- assert not argument.prop.processed_input, "EmbeddingEngine does not support processed_input."
75
+ assert not argument.prop.processed_input, (
76
+ "EmbeddingEngine does not support processed_input."
77
+ )
73
78
  argument.prop.prepared_input = argument.prop.entries
74
79
 
75
80
  def _normalize_l2(self, x):
@@ -79,6 +84,5 @@ class EmbeddingEngine(Engine, OpenAIMixin):
79
84
  if norm == 0:
80
85
  return x.tolist()
81
86
  return (x / norm).tolist()
82
- else:
83
- norm = np.linalg.norm(x, 2, axis=1, keepdims=True)
84
- return np.where(norm == 0, x, x / norm).tolist()
87
+ norm = np.linalg.norm(x, 2, axis=1, keepdims=True)
88
+ return np.where(norm == 0, x, x / norm).tolist()
@@ -1,19 +1,20 @@
1
+ import sys
2
+ import traceback
3
+
1
4
  from ....symbol import Result
2
5
  from ...base import Engine
3
6
 
4
7
 
5
8
  def full_stack():
6
- import sys
7
- import traceback
8
9
  exc = sys.exc_info()[0]
9
10
  stack = traceback.extract_stack()[-10:-1] # last one would be full_stack()
10
- if exc is not None: # i.e. an exception is present
11
- del stack[-1] # remove call of full_stack, the printed exception
12
- # will contain the caught exception caller instead
13
- trc = 'Traceback (most recent call last):\n'
14
- stackstr = trc + ''.join(traceback.format_list(stack))
11
+ if exc is not None: # i.e. an exception is present
12
+ del stack[-1] # remove call of full_stack, the printed exception
13
+ # will contain the caught exception caller instead
14
+ trc = "Traceback (most recent call last):\n"
15
+ stackstr = trc + "".join(traceback.format_list(stack))
15
16
  if exc is not None:
16
- stackstr += ' ' + traceback.format_exc().lstrip(trc)
17
+ stackstr += " " + traceback.format_exc().lstrip(trc)
17
18
  return stackstr
18
19
 
19
20
 
@@ -63,14 +64,14 @@ class PythonEngine(Engine):
63
64
  self.name = self.__class__.__name__
64
65
 
65
66
  def id(self) -> str:
66
- return 'execute'
67
+ return "execute"
67
68
 
68
69
  def forward(self, argument):
69
70
  code = argument.prop.prepared_input
70
71
  kwargs = argument.kwargs
71
- globals_ = kwargs['globals'] if 'globals' in kwargs else {}
72
- locals_ = kwargs['locals'] if 'locals' in kwargs else {}
73
- input_handler = kwargs['input_handler'] if 'input_handler' in kwargs else None
72
+ globals_ = kwargs.get("globals", {})
73
+ locals_ = kwargs.get("locals", {})
74
+ input_handler = kwargs.get("input_handler")
74
75
  if input_handler:
75
76
  input_handler((code,))
76
77
 
@@ -78,18 +79,18 @@ class PythonEngine(Engine):
78
79
  err = None
79
80
  try:
80
81
  exec(str(code), globals_, locals_)
81
- rsp = {'globals': globals_, 'locals': locals_}
82
- if 'res' in locals_:
83
- rsp['locals_res'] = locals_['res']
84
- if 'res' in globals_:
85
- rsp['globals_res'] = globals_['res']
82
+ rsp = {"globals": globals_, "locals": locals_}
83
+ if "res" in locals_:
84
+ rsp["locals_res"] = locals_["res"]
85
+ if "res" in globals_:
86
+ rsp["globals_res"] = globals_["res"]
86
87
  rsp = Result(rsp)
87
88
  except Exception as e:
88
89
  err = e
89
90
  raise e
90
91
 
91
92
  metadata = {}
92
- metadata['error'] = None if not err else full_stack()
93
+ metadata["error"] = None if not err else full_stack()
93
94
 
94
95
  return [rsp], metadata
95
96