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
symai/core_ext.py CHANGED
@@ -26,6 +26,7 @@ logging.getLogger("multiprocessing").setLevel(logging.ERROR)
26
26
  _pool_registry: dict[int, mp.pool.Pool] = {}
27
27
  _pool_lock = threading.Lock()
28
28
 
29
+
29
30
  def _get_pool(workers: int) -> mp.pool.Pool:
30
31
  pool = _pool_registry.get(workers)
31
32
  if pool is not None:
@@ -37,28 +38,33 @@ def _get_pool(workers: int) -> mp.pool.Pool:
37
38
  _pool_registry[workers] = pool
38
39
  return pool
39
40
 
41
+
40
42
  @atexit.register
41
43
  def _shutdown_pools() -> None:
42
44
  for pool in _pool_registry.values():
43
45
  pool.close()
44
46
  pool.join()
45
47
 
48
+
46
49
  def _run_in_process(expr, func, args, kwargs):
47
50
  expr = dill.loads(expr)
48
51
  func = dill.loads(func)
49
52
  return func(expr, *args, **kwargs)
50
53
 
54
+
51
55
  def _parallel(func: Callable, expressions: list[Callable], worker: int = mp.cpu_count() // 2):
52
56
  pickled_exprs = [dill.dumps(expr) for expr in expressions]
53
- pickled_func = dill.dumps(func)
57
+ pickled_func = dill.dumps(func)
54
58
  pool = _get_pool(worker)
59
+
55
60
  def proxy_function(*args, **kwargs):
56
61
  return pool.starmap(
57
- _run_in_process,
58
- [(expr, pickled_func, args, kwargs) for expr in pickled_exprs]
59
- )
62
+ _run_in_process, [(expr, pickled_func, args, kwargs) for expr in pickled_exprs]
63
+ )
64
+
60
65
  return proxy_function
61
66
 
67
+
62
68
  # Decorator
63
69
  def parallel(expressions: list[Callable], worker: int = mp.cpu_count() // 2):
64
70
  def decorator_parallel(func):
@@ -68,34 +74,31 @@ def parallel(expressions: list[Callable], worker: int = mp.cpu_count() // 2):
68
74
  parallel_func = _parallel(func, expressions, worker=worker)
69
75
  # Call the proxy function to execute in parallel and capture results
70
76
  return parallel_func(*args, **kwargs)
77
+
71
78
  return wrapper
79
+
72
80
  return decorator_parallel
73
81
 
82
+
74
83
  def bind(engine: str, property: str):
75
- '''
84
+ """
76
85
  Bind to an engine and retrieve one of its properties.
77
- '''
86
+ """
87
+
78
88
  def decorator(func):
79
89
  @functools.wraps(func)
80
90
  def wrapper(*_args, **_kwargs):
81
- return EngineRepository.bind_property(
82
- engine=engine,
83
- property=property
84
- )
91
+ return EngineRepository.bind_property(engine=engine, property=property)
92
+
85
93
  return wrapper
94
+
86
95
  return decorator
87
96
 
88
97
 
89
98
  def retry(
90
- exceptions=Exception,
91
- tries=-1,
92
- delay=0,
93
- max_delay=-1,
94
- backoff=1,
95
- jitter=0,
96
- graceful=False
99
+ exceptions=Exception, tries=-1, delay=0, max_delay=-1, backoff=1, jitter=0, graceful=False
97
100
  ):
98
- '''
101
+ """
99
102
  Returns a retry decorator for both async and sync functions.
100
103
 
101
104
  :param exceptions: an exception or a tuple of exceptions to catch. default: Exception.
@@ -108,9 +111,11 @@ def retry(
108
111
  :param graceful: whether to raise an exception if all attempts fail or return with None. default: False.
109
112
 
110
113
  Credits to invlpg (https://pypi.org/project/retry)
111
- '''
114
+ """
115
+
112
116
  def decorator(func):
113
117
  if asyncio.iscoroutinefunction(func):
118
+
114
119
  @functools.wraps(func)
115
120
  async def async_wrapper(*args, **kwargs):
116
121
  return await _aretry_func(
@@ -121,9 +126,11 @@ def retry(
121
126
  max_delay=max_delay,
122
127
  backoff=backoff,
123
128
  jitter=jitter,
124
- graceful=graceful
129
+ graceful=graceful,
125
130
  )
131
+
126
132
  return async_wrapper
133
+
127
134
  @functools.wraps(func)
128
135
  def sync_wrapper(*args, **kwargs):
129
136
  return _retry_func(
@@ -134,9 +141,11 @@ def retry(
134
141
  max_delay=max_delay,
135
142
  backoff=backoff,
136
143
  jitter=jitter,
137
- graceful=graceful
144
+ graceful=graceful,
138
145
  )
146
+
139
147
  return sync_wrapper
148
+
140
149
  return decorator
141
150
 
142
151
 
@@ -148,7 +157,7 @@ def _retry_func(
148
157
  max_delay: int,
149
158
  backoff: int,
150
159
  jitter: int,
151
- graceful: bool
160
+ graceful: bool,
152
161
  ):
153
162
  _tries, _delay = tries, delay
154
163
  while _tries:
@@ -182,7 +191,7 @@ async def _aretry_func(
182
191
  max_delay: int,
183
192
  backoff: int,
184
193
  jitter: int,
185
- graceful: bool
194
+ graceful: bool,
186
195
  ):
187
196
  _tries, _delay = tries, delay
188
197
  while _tries:
@@ -208,52 +217,42 @@ async def _aretry_func(
208
217
  return None
209
218
 
210
219
 
211
- def cache(
212
- in_memory: bool,
213
- cache_path: str = __root_dir__ / 'cache'
214
- ):
215
- '''
220
+ def cache(in_memory: bool, cache_path: str = __root_dir__ / "cache"):
221
+ """
216
222
  Cache the result of a *any* function call. This is very useful in cost optimization (e.g. computing embeddings).
217
- '''
223
+ """
224
+
218
225
  def decorator(func):
219
226
  @functools.wraps(func)
220
227
  def wrapper(instance):
221
- return _cache_registry_func(
222
- in_memory,
223
- cache_path,
224
- func,
225
- instance
226
- )
228
+ return _cache_registry_func(in_memory, cache_path, func, instance)
229
+
227
230
  return wrapper
228
- return decorator
229
231
 
232
+ return decorator
230
233
 
231
- def _cache_registry_func(
232
- in_memory: bool,
233
- cache_path: str,
234
- func: Callable,
235
- *args, **kwargs
236
- ):
237
234
 
235
+ def _cache_registry_func(in_memory: bool, cache_path: str, func: Callable, *args, **kwargs):
238
236
  cache_dir = Path(cache_path)
239
237
  cache_dir.mkdir(parents=True, exist_ok=True)
240
238
  cache_file = cache_dir / func.__qualname__
241
239
 
242
240
  if in_memory and cache_file.exists():
243
- with cache_file.open('rb') as f:
241
+ with cache_file.open("rb") as f:
244
242
  return pickle.load(f)
245
243
 
246
244
  call = func(*args, **kwargs)
247
- with cache_file.open('wb') as f:
248
- pickle.dump(call , f)
245
+ with cache_file.open("wb") as f:
246
+ pickle.dump(call, f)
249
247
 
250
248
  return call
251
249
 
252
250
 
253
251
  def error_logging(debug: bool = False):
254
- '''
252
+ """
255
253
  Log the error of a function call.
256
- '''
254
+ """
255
+
257
256
  def dec(func):
258
257
  @functools.wraps(func)
259
258
  def _dec(*args, **kwargs):
@@ -263,11 +262,13 @@ def error_logging(debug: bool = False):
263
262
  logger.error(e)
264
263
  if debug:
265
264
  # Simple message:
266
- UserMessage(f'Function: {func.__name__} call failed. Error: {e}')
265
+ UserMessage(f"Function: {func.__name__} call failed. Error: {e}")
267
266
  # print traceback
268
267
  traceback.print_exc()
269
268
  raise e
269
+
270
270
  return _dec
271
+
271
272
  return dec
272
273
 
273
274
 
@@ -289,18 +290,21 @@ def deprecated(reason: str = ""):
289
290
  @deprecated("Use new_method() instead")
290
291
  def old_method(self): pass
291
292
  """
293
+
292
294
  def decorator(obj):
293
295
  if isinstance(obj, type):
294
296
  # If obj is a class
295
297
  original_init = obj.__init__
298
+
296
299
  @functools.wraps(original_init)
297
300
  def new_init(self, *args, **kwargs):
298
301
  logger.warning(
299
302
  f"{obj.__name__} is deprecated and will be removed in future versions. {reason}",
300
303
  category=DeprecationWarning,
301
- stacklevel=2
304
+ stacklevel=2,
302
305
  )
303
306
  original_init(self, *args, **kwargs)
307
+
304
308
  obj.__init__ = new_init
305
309
  return obj
306
310
 
@@ -310,9 +314,10 @@ def deprecated(reason: str = ""):
310
314
  logger.warning(
311
315
  f"{obj.__name__} is deprecated and will be removed in future versions. {reason}",
312
316
  category=DeprecationWarning,
313
- stacklevel=2
317
+ stacklevel=2,
314
318
  )
315
319
  return obj(*args, **kwargs)
320
+
316
321
  return wrapper
317
322
 
318
323
  return decorator
symai/endpoints/api.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import importlib
2
2
  import inspect
3
3
  import pickle
4
- from typing import Any
4
+ from typing import Any, Generic, TypeVar
5
5
 
6
6
  import redis
7
7
  from fastapi import APIRouter, FastAPI, HTTPException, Security, status
@@ -17,10 +17,11 @@ from ..symbol import Expression, Symbol
17
17
  from ..utils import UserMessage
18
18
 
19
19
  # Configure Redis server connection parameters and executable path
20
- HOST = 'localhost'
21
- PORT = 6379
20
+ HOST = "localhost"
21
+ PORT = 6379
22
22
  DEBUG = True
23
- API_KEY = settings.SYMAI_CONFIG.get('FASTAPI_API_KEY', None)
23
+ API_KEY = settings.SYMAI_CONFIG.get("FASTAPI_API_KEY", None)
24
+
24
25
 
25
26
  def is_redis_running(host: str, port: int) -> bool:
26
27
  """Check if a Redis server is running at the given host and port."""
@@ -30,26 +31,31 @@ def is_redis_running(host: str, port: int) -> bool:
30
31
  UserMessage(f"Redis server is running at {host}:{port}")
31
32
  return True
32
33
  except RedisConnectionError:
33
- UserMessage(f"Redis server is not running at {host}:{port} or is not reachable - falling back to in-memory storage")
34
+ UserMessage(
35
+ f"Redis server is not running at {host}:{port} or is not reachable - falling back to in-memory storage"
36
+ )
34
37
  return False
35
38
 
36
39
 
37
- class GenericRepository[T]:
40
+ T = TypeVar("T")
41
+
42
+
43
+ class GenericRepository(Generic[T]):
38
44
  def __init__(self, redis_client: redis.Redis, id_key: str, use_redis: bool = True):
39
45
  self.storage: dict[str, T] = {} # In-memory dictionary to mock Redis
40
- self.use_redis = use_redis
41
- self.id_key = id_key # Key used for storing the incremental ID counter
42
- self.redis_client = redis_client
46
+ self.use_redis = use_redis
47
+ self.id_key = id_key # Key used for storing the incremental ID counter
48
+ self.redis_client = redis_client
43
49
  if self.use_redis:
44
- self.set = self._store_redis
45
- self.get = self._retrieve_redis
46
- self.delete = self._delete_redis
47
- self.uid = self._generate_id_redis
50
+ self.set = self._store_redis
51
+ self.get = self._retrieve_redis
52
+ self.delete = self._delete_redis
53
+ self.uid = self._generate_id_redis
48
54
  else:
49
- self.set = self._store_memory
50
- self.get = self._retrieve_memory
51
- self.delete = self._delete_memory
52
- self.uid = self._generate_id_memory
55
+ self.set = self._store_memory
56
+ self.get = self._retrieve_memory
57
+ self.delete = self._delete_memory
58
+ self.uid = self._generate_id_memory
53
59
 
54
60
  def _deserialize_object(self, serialized_item: bytes) -> T:
55
61
  """Deserialize the byte object back into a Python object of type T."""
@@ -72,7 +78,7 @@ class GenericRepository[T]:
72
78
 
73
79
  def _generate_id_memory(self) -> str:
74
80
  id_ = len(self.storage) + 1
75
- return f'{self.id_key}:{id_}'
81
+ return f"{self.id_key}:{id_}"
76
82
 
77
83
  def _store_redis(self, item_id: str, item: T) -> None:
78
84
  item = self._serialize_object(item_id, item)
@@ -89,38 +95,44 @@ class GenericRepository[T]:
89
95
 
90
96
  def _generate_id_redis(self) -> str:
91
97
  id_ = self.redis_client.incr(self.id_key)
92
- return f'{self.id_key}:{id_}'
98
+ return f"{self.id_key}:{id_}"
93
99
 
94
100
 
95
101
  # Initialize the Redis client
96
102
  redis_client = redis.Redis(
97
- host=HOST, # Or use the host where your Redis server is running
98
- port=PORT, # The default Redis port number
99
- db=0 # The default database index
103
+ host=HOST, # Or use the host where your Redis server is running
104
+ port=PORT, # The default Redis port number
105
+ db=0, # The default database index
100
106
  )
101
107
 
102
108
  # Initialize the FastAPI app and API router
103
- app = FastAPI(title="SymbolicAI API", version="1.0")
104
- api_key_header = APIKeyHeader(name="X-API-Key")
105
- router = APIRouter()
106
- use_redis = is_redis_running(HOST, PORT)
109
+ app = FastAPI(title="SymbolicAI API", version="1.0")
110
+ api_key_header = APIKeyHeader(name="X-API-Key")
111
+ router = APIRouter()
112
+ use_redis = is_redis_running(HOST, PORT)
107
113
 
108
114
  # Instantiate the generic repositories with the counter keys
109
- symbol_repository = GenericRepository[Symbol](redis_client, "sym_id", use_redis=use_redis)
110
- expression_repository = GenericRepository[Expression](redis_client, "expr_id", use_redis=use_redis)
115
+ symbol_repository = GenericRepository[Symbol](redis_client, "sym_id", use_redis=use_redis)
116
+ expression_repository = GenericRepository[Expression](redis_client, "expr_id", use_redis=use_redis)
111
117
  # Register all types which subclass Expression and offer a get() method with default=None
112
118
  component_class_types = {
113
- name: cls for name, cls in inspect.getmembers(importlib.import_module('symai.components'), inspect.isclass) if issubclass(cls, Expression)
119
+ name: cls
120
+ for name, cls in inspect.getmembers(
121
+ importlib.import_module("symai.components"), inspect.isclass
122
+ )
123
+ if issubclass(cls, Expression)
114
124
  }
115
- component_instance_repository = GenericRepository[Symbol | Expression](redis_client, "comp_id", use_redis=use_redis)
125
+ component_instance_repository = GenericRepository[Symbol | Expression](
126
+ redis_client, "comp_id", use_redis=use_redis
127
+ )
116
128
 
117
129
 
118
130
  def _load_extended_types():
119
131
  """Load extended classes lazily to avoid premature engine initialization."""
120
- extended_module = importlib.import_module('symai.extended')
121
- personas_module = importlib.import_module('symai.extended.personas')
122
- sales_module = importlib.import_module('symai.extended.personas.sales')
123
- student_module = importlib.import_module('symai.extended.personas.student')
132
+ extended_module = importlib.import_module("symai.extended")
133
+ personas_module = importlib.import_module("symai.extended.personas")
134
+ sales_module = importlib.import_module("symai.extended.personas.sales")
135
+ student_module = importlib.import_module("symai.extended.personas.student")
124
136
  return [
125
137
  extended_module.Conversation,
126
138
  personas_module.Persona,
@@ -129,14 +141,21 @@ def _load_extended_types():
129
141
  sales_module.ErikJames,
130
142
  ]
131
143
 
132
- app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
144
+
145
+ app.add_middleware(
146
+ CORSMiddleware,
147
+ allow_origins=["*"],
148
+ allow_credentials=True,
149
+ allow_methods=["*"],
150
+ allow_headers=["*"],
151
+ )
133
152
 
134
153
  ### Symbols Endpoints ###
135
154
 
136
155
 
137
156
  class CreateSymbolRequest(BaseModel):
138
- value: Any | None = None
139
- static_context: str | None = ''
157
+ value: Any | None = None
158
+ static_context: str | None = ""
140
159
 
141
160
 
142
161
  class UpdateSymbolRequest(BaseModel):
@@ -148,6 +167,7 @@ class SymbolMethodRequest(BaseModel):
148
167
  args: list[Any] = []
149
168
  kwargs: dict[str, Any] = {}
150
169
 
170
+
151
171
  def get_api_key(api_key_header: str = Security(api_key_header) if API_KEY else None) -> str:
152
172
  if API_KEY is None:
153
173
  return True
@@ -158,6 +178,7 @@ def get_api_key(api_key_header: str = Security(api_key_header) if API_KEY else N
158
178
  detail="Invalid or missing API Key",
159
179
  )
160
180
 
181
+
161
182
  @app.post("/symbol/")
162
183
  def create_symbol(symbol_request: CreateSymbolRequest, _api_key: str = Security(get_api_key)):
163
184
  symbol = Symbol(symbol_request.value, static_context=symbol_request.static_context)
@@ -175,7 +196,9 @@ def get_symbol(symbol_id: str, _api_key: str = Security(get_api_key)):
175
196
 
176
197
 
177
198
  @app.patch("/symbol/{symbol_id}/")
178
- def update_symbol(symbol_id: str, update_request: UpdateSymbolRequest, _api_key: str = Security(get_api_key)):
199
+ def update_symbol(
200
+ symbol_id: str, update_request: UpdateSymbolRequest, _api_key: str = Security(get_api_key)
201
+ ):
179
202
  symbol = symbol_repository.get(symbol_id)
180
203
  if symbol is None:
181
204
  raise HTTPException(status_code=404, detail="Symbol not found")
@@ -195,13 +218,20 @@ def delete_symbol(symbol_id: str, _api_key: str = Security(get_api_key)):
195
218
 
196
219
  @app.post("/symbol/{symbol_id}/{method_name}/")
197
220
  @core_ext.error_logging(debug=DEBUG)
198
- def operate_on_symbol(symbol_id: str, method_name: str, method_request: SymbolMethodRequest, _api_key: str = Security(get_api_key)):
221
+ def operate_on_symbol(
222
+ symbol_id: str,
223
+ method_name: str,
224
+ method_request: SymbolMethodRequest,
225
+ _api_key: str = Security(get_api_key),
226
+ ):
199
227
  symbol = symbol_repository.get(symbol_id)
200
228
  if symbol is None:
201
229
  raise HTTPException(status_code=404, detail="Symbol not found")
202
230
  method = getattr(symbol, method_name, None)
203
231
  if method is None or not callable(method):
204
- raise HTTPException(status_code=404, detail=f"Method {method_name} not found or is not callable")
232
+ raise HTTPException(
233
+ status_code=404, detail=f"Method {method_name} not found or is not callable"
234
+ )
205
235
  result = method(*method_request.args, **method_request.kwargs)
206
236
  return {"result": result.json() if isinstance(result, Symbol) else result}
207
237
 
@@ -218,7 +248,9 @@ class UpdateExpressionRequest(BaseModel):
218
248
 
219
249
 
220
250
  @app.post("/expression/")
221
- def create_expression(expression_request: CreateExpressionRequest, _api_key: str = Security(get_api_key)):
251
+ def create_expression(
252
+ expression_request: CreateExpressionRequest, _api_key: str = Security(get_api_key)
253
+ ):
222
254
  expression = Expression(expression_request.value)
223
255
  expression_id = expression_repository.uid()
224
256
  expression_repository.set(expression_id, expression)
@@ -235,7 +267,9 @@ def get_expression(expression_id: str, _api_key: str = Security(get_api_key)):
235
267
 
236
268
  @app.post("/expression/{expression_id}/call/")
237
269
  @core_ext.error_logging(debug=DEBUG)
238
- def call_expression(expression_id: str, method_request: SymbolMethodRequest, _api_key: str = Security(get_api_key)):
270
+ def call_expression(
271
+ expression_id: str, method_request: SymbolMethodRequest, _api_key: str = Security(get_api_key)
272
+ ):
239
273
  # Retrieve the expression instance by ID
240
274
  expression = expression_repository.get(expression_id)
241
275
  if expression is None:
@@ -246,19 +280,30 @@ def call_expression(expression_id: str, method_request: SymbolMethodRequest, _ap
246
280
 
247
281
  @app.post("/expression/{expression_id}/{method_name}/")
248
282
  @core_ext.error_logging(debug=DEBUG)
249
- def operate_on_expression(expression_id: str, method_name: str, method_request: SymbolMethodRequest, _api_key: str = Security(get_api_key)):
283
+ def operate_on_expression(
284
+ expression_id: str,
285
+ method_name: str,
286
+ method_request: SymbolMethodRequest,
287
+ _api_key: str = Security(get_api_key),
288
+ ):
250
289
  expression = expression_repository.get(expression_id)
251
290
  if expression is None:
252
291
  raise HTTPException(status_code=404, detail="Expression not found")
253
292
  method = getattr(expression, method_name, None)
254
293
  if method is None or not callable(method):
255
- raise HTTPException(status_code=404, detail=f"Method {method_name} not found or is not callable")
294
+ raise HTTPException(
295
+ status_code=404, detail=f"Method {method_name} not found or is not callable"
296
+ )
256
297
  result = method(*method_request.args, **method_request.kwargs)
257
298
  return {"result": result.json() if isinstance(result, Symbol) else result}
258
299
 
259
300
 
260
301
  @app.patch("/expression/{expression_id}/")
261
- def update_expression(expression_id: str, update_request: UpdateExpressionRequest, _api_key: str = Security(get_api_key)):
302
+ def update_expression(
303
+ expression_id: str,
304
+ update_request: UpdateExpressionRequest,
305
+ _api_key: str = Security(get_api_key),
306
+ ):
262
307
  expression = expression_repository.get(expression_id)
263
308
  if expression is None:
264
309
  raise HTTPException(status_code=404, detail="Expression not found")
@@ -288,17 +333,17 @@ class AddComponentRequest(BaseModel):
288
333
  # Endpoint to instantiate a generic component class and get the ID
289
334
  class CreateComponentGenericRequest(BaseModel):
290
335
  class_name: str
291
- init_args: list[Any] = []
336
+ init_args: list[Any] = []
292
337
  init_kwargs: dict[str, Any] = {}
293
338
 
294
339
 
295
340
  # Modify the existing GenericRequest
296
341
  class GenericRequest(BaseModel):
297
342
  class_name: str
298
- init_args: list[Any] = {}
299
- init_kwargs: dict[str, Any] = {}
300
- forward_args: list[ Any] = {}
301
- forward_kwargs: dict[str, Any] = {}
343
+ init_args: list[Any] = {}
344
+ init_kwargs: dict[str, Any] = {}
345
+ forward_args: list[Any] = {}
346
+ forward_kwargs: dict[str, Any] = {}
302
347
 
303
348
 
304
349
  class UpdateComponentRequest(BaseModel):
@@ -315,7 +360,9 @@ def create_component(request: CreateComponentGenericRequest, _api_key: str = Sec
315
360
  instance = cls(*request.init_args, **request.init_kwargs)
316
361
  # Store the instance with a generated ID and return the ID
317
362
  instance_id = component_instance_repository.uid()
318
- component_instance_repository.set(instance_id, instance) # Assuming component_instance_repository exists
363
+ component_instance_repository.set(
364
+ instance_id, instance
365
+ ) # Assuming component_instance_repository exists
319
366
  return {"id": instance_id}
320
367
 
321
368
 
@@ -333,7 +380,7 @@ def get_component(instance_id: str, _api_key: str = Security(get_api_key)):
333
380
  @core_ext.error_logging(debug=DEBUG)
334
381
  def generic_forward(request: GenericRequest, _api_key: str = Security(get_api_key)):
335
382
  # Dynamically import the class from components module based on request.class_name
336
- components_module = importlib.import_module('.components', package='symai')
383
+ components_module = importlib.import_module(".components", package="symai")
337
384
  cls = getattr(components_module, request.class_name)
338
385
  # Check if cls is subclass of Expression and instantiate
339
386
  if not issubclass(cls, components_module.Expression):
@@ -351,7 +398,9 @@ def generic_forward(request: GenericRequest, _api_key: str = Security(get_api_ke
351
398
 
352
399
 
353
400
  @app.patch("/components/{instance_id}/")
354
- def update_component(instance_id: str, update_request: UpdateComponentRequest, _api_key: str = Security(get_api_key)):
401
+ def update_component(
402
+ instance_id: str, update_request: UpdateComponentRequest, _api_key: str = Security(get_api_key)
403
+ ):
355
404
  instance = component_instance_repository.get(instance_id)
356
405
  if instance is None:
357
406
  raise HTTPException(status_code=404, detail="Component instance not found")
@@ -374,14 +423,16 @@ def delete_component(instance_id: str, _api_key: str = Security(get_api_key)):
374
423
  # extended types loaded lazily to avoid early engine configuration
375
424
  extended_types = _load_extended_types()
376
425
  # Register only the extended types from the extended_types Union
377
- extended_class_types = {c.__name__: c for c in extended_types}
378
- extended_instance_repository = GenericRepository[extended_types](redis_client, "ext_id", use_redis=use_redis)
426
+ extended_class_types = {c.__name__: c for c in extended_types}
427
+ extended_instance_repository = GenericRepository[extended_types](
428
+ redis_client, "ext_id", use_redis=use_redis
429
+ )
379
430
 
380
431
 
381
432
  # Model definitions for extended classes
382
433
  class CreateExtendedRequest(BaseModel):
383
434
  class_name: str
384
- init_args: list[Any] = []
435
+ init_args: list[Any] = []
385
436
  init_kwargs: dict[str, Any] = {}
386
437
 
387
438
 
@@ -447,7 +498,9 @@ def get_extended(instance_id: str, _api_key: str = Security(get_api_key)):
447
498
 
448
499
 
449
500
  @app.patch("/extended/{instance_id}/")
450
- def update_extended(instance_id: str, update_request: UpdateExtendedRequest, _api_key: str = Security(get_api_key)):
501
+ def update_extended(
502
+ instance_id: str, update_request: UpdateExtendedRequest, _api_key: str = Security(get_api_key)
503
+ ):
451
504
  # Retrieve the instance by its ID
452
505
  extended_instance = extended_instance_repository.get(instance_id)
453
506
  if extended_instance is None:
@@ -464,7 +517,9 @@ def delete_extended(instance_id: str, _api_key: str = Security(get_api_key)):
464
517
  # Attempt to delete the instance by its ID
465
518
  success = extended_instance_repository.delete(instance_id)
466
519
  if not success:
467
- raise HTTPException(status_code=404, detail="Extended instance not found or already deleted")
520
+ raise HTTPException(
521
+ status_code=404, detail="Extended instance not found or already deleted"
522
+ )
468
523
  return {"message": "Extended instance deleted successfully"}
469
524
 
470
525