symbolicai 1.4.0__py3-none-any.whl → 1.6.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 (54) hide show
  1. symai/__init__.py +21 -71
  2. symai/backend/base.py +0 -26
  3. symai/backend/engines/drawing/engine_gemini_image.py +101 -0
  4. symai/backend/engines/embedding/engine_openai.py +11 -8
  5. symai/backend/engines/neurosymbolic/__init__.py +8 -0
  6. symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +14 -1
  7. symai/backend/engines/neurosymbolic/engine_openrouter.py +294 -0
  8. symai/backend/engines/scrape/engine_requests.py +39 -10
  9. symai/backend/engines/search/__init__.py +13 -0
  10. symai/backend/engines/search/engine_firecrawl.py +333 -0
  11. symai/backend/engines/search/engine_parallel.py +5 -5
  12. symai/backend/mixin/__init__.py +4 -0
  13. symai/backend/mixin/openrouter.py +2 -0
  14. symai/components.py +212 -16
  15. symai/extended/interfaces/firecrawl.py +30 -0
  16. symai/extended/interfaces/nanobanana.py +23 -0
  17. symai/extended/interfaces/parallel.py +5 -5
  18. symai/functional.py +3 -4
  19. symai/interfaces.py +2 -0
  20. symai/ops/primitives.py +0 -18
  21. symai/shellsv.py +2 -7
  22. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/METADATA +3 -9
  23. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/RECORD +27 -47
  24. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/WHEEL +1 -1
  25. symai/backend/driver/webclient.py +0 -217
  26. symai/backend/engines/crawler/engine_selenium.py +0 -94
  27. symai/backend/engines/drawing/engine_dall_e.py +0 -131
  28. symai/backend/engines/embedding/engine_plugin_embeddings.py +0 -12
  29. symai/backend/engines/experiments/engine_bard_wrapper.py +0 -131
  30. symai/backend/engines/experiments/engine_gptfinetuner.py +0 -32
  31. symai/backend/engines/experiments/engine_llamacpp_completion.py +0 -142
  32. symai/backend/engines/neurosymbolic/engine_openai_gptX_completion.py +0 -277
  33. symai/collect/__init__.py +0 -8
  34. symai/collect/dynamic.py +0 -117
  35. symai/collect/pipeline.py +0 -156
  36. symai/collect/stats.py +0 -434
  37. symai/extended/crawler.py +0 -21
  38. symai/extended/interfaces/selenium.py +0 -18
  39. symai/extended/interfaces/vectordb.py +0 -21
  40. symai/extended/personas/__init__.py +0 -3
  41. symai/extended/personas/builder.py +0 -105
  42. symai/extended/personas/dialogue.py +0 -126
  43. symai/extended/personas/persona.py +0 -154
  44. symai/extended/personas/research/__init__.py +0 -1
  45. symai/extended/personas/research/yann_lecun.py +0 -62
  46. symai/extended/personas/sales/__init__.py +0 -1
  47. symai/extended/personas/sales/erik_james.py +0 -62
  48. symai/extended/personas/student/__init__.py +0 -1
  49. symai/extended/personas/student/max_tenner.py +0 -51
  50. symai/extended/strategies/__init__.py +0 -1
  51. symai/extended/strategies/cot.py +0 -40
  52. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/entry_points.txt +0 -0
  53. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/licenses/LICENSE +0 -0
  54. {symbolicai-1.4.0.dist-info → symbolicai-1.6.0.dist-info}/top_level.txt +0 -0
symai/collect/stats.py DELETED
@@ -1,434 +0,0 @@
1
- import json
2
- import re
3
- from collections.abc import Callable
4
- from json import JSONEncoder
5
- from pathlib import Path
6
- from typing import Any, Union
7
-
8
- import numpy as np
9
- import torch
10
-
11
- from ..ops.primitives import OperatorPrimitives
12
- from ..symbol import Symbol
13
- from ..utils import UserMessage
14
-
15
- SPECIAL_CONSTANT = "__aggregate_"
16
- EXCLUDE_LIST = ["_ipython_canary_method_should_not_exist_", "__custom_documentations__"]
17
-
18
-
19
- def _normalize_name(name: str) -> str:
20
- # Replace any character that is not a letter or a number with an underscore
21
- normalized_name = re.sub(r"[^a-zA-Z0-9]", "_", name)
22
- return normalized_name.lower()
23
-
24
-
25
- class AggregatorJSONEncoder(JSONEncoder):
26
- def default(self, obj):
27
- if isinstance(obj, np.ndarray):
28
- return obj.tolist()
29
- # drop active from state
30
- if isinstance(obj, Aggregator):
31
- state = obj.__dict__.copy()
32
- state.pop("_raise_error", None)
33
- state.pop("_active", None)
34
- state.pop("_finalized", None)
35
- state.pop("_map", None)
36
- # drop everything that starts with SPECIAL_CONSTANT
37
- for key in list(state.keys()):
38
- if (
39
- (not key.startswith(SPECIAL_CONSTANT) and key != "_value")
40
- or (key == "_value" and obj._value == [])
41
- or key.replace(SPECIAL_CONSTANT, "") in EXCLUDE_LIST
42
- ):
43
- state.pop(key, None)
44
- return state
45
- return obj.__dict__
46
-
47
-
48
- class Aggregator(Symbol):
49
- def __init__(
50
- self,
51
- value: Union["Aggregator", Symbol] | None = None,
52
- path: str | None = None,
53
- active: bool = True,
54
- raise_error: bool = False,
55
- *args,
56
- **kwargs,
57
- ):
58
- super().__init__(*args, **kwargs)
59
- # disable nesy engine to avoid side effects
60
- self.__disable_nesy_engine__ = True
61
- if value is not None and isinstance(value, Symbol):
62
- # use this to avoid recursion on map setter
63
- self._value = value._value
64
- if isinstance(self._value, np.ndarray):
65
- self._value = self._value.tolist()
66
- elif isinstance(self._value, torch.Tensor):
67
- self._value = self._value.detach().cpu().numpy().tolist()
68
- elif not isinstance(self._value, (list, tuple)):
69
- self._value = [self._value]
70
- elif value is not None:
71
- UserMessage(
72
- f"Aggregator object must be of type Aggregator or Symbol! Got: {type(value)}",
73
- raise_with=Exception,
74
- )
75
- else:
76
- self._value = []
77
- self._raise_error = raise_error
78
- self._active = active
79
- self._finalized = False
80
- self._map = None
81
- self._path = path
82
-
83
- def __new__(
84
- cls,
85
- *args,
86
- mixin: bool | None = None,
87
- primitives: list[type] | None = None, # only inherit arithmetic primitives
88
- callables: list[tuple[str, Callable]] | None = None,
89
- semantic: bool = False,
90
- **kwargs,
91
- ) -> "Symbol":
92
- if primitives is None:
93
- primitives = [OperatorPrimitives]
94
- return super().__new__(
95
- cls,
96
- *args,
97
- mixin=mixin,
98
- primitives=primitives,
99
- callables=callables,
100
- semantic=semantic,
101
- **kwargs,
102
- )
103
-
104
- def __getattr__(self, name):
105
- # replace name special characters and spaces with underscores
106
- name = _normalize_name(name)
107
- # Dynamically create new aggregator instance if it does not exist
108
- if self._active and name not in self.__dict__:
109
- aggregator = Aggregator(path=name)
110
- aggregator._parent = self
111
- self._children.append(aggregator)
112
- # create a new aggregate aggregator
113
- # named {SPECIAL_CONSTANT}{name} for automatic aggregation
114
- self.__dict__[f"{SPECIAL_CONSTANT}{name}"] = aggregator
115
- # add also a property with the same name but without the SPECIAL_CONSTANT prefix as a shortcut
116
- self.__dict__[name] = self.__dict__[f"{SPECIAL_CONSTANT}{name}"]
117
- return self.__dict__.get(name)
118
- if not self._active and name not in self.__dict__:
119
- UserMessage(
120
- f"Aggregator object is frozen! No attribute {name} found!", raise_with=Exception
121
- )
122
- return self.__dict__.get(name)
123
-
124
- def __setattr__(self, name, value):
125
- # replace name special characters and spaces with underscores
126
- name = _normalize_name(name)
127
- return super().__setattr__(name, value)
128
-
129
- def __delattr__(self, name):
130
- # replace name special characters and spaces with underscores
131
- name = _normalize_name(name)
132
- return super().__delattr__(name)
133
-
134
- def __getitem__(self, name):
135
- # replace name special characters and spaces with underscores
136
- name = _normalize_name(name)
137
- return self.__getattr__(name)
138
-
139
- def __setitem__(self, name, value):
140
- # replace name special characters and spaces with underscores
141
- name = _normalize_name(name)
142
- return self.__setattr__(name, value)
143
-
144
- def __delitem__(self, name):
145
- # replace name special characters and spaces with underscores
146
- name = _normalize_name(name)
147
- return self.__delattr__(name)
148
-
149
- def __setstate__(self, state):
150
- # replace name special characters and spaces with underscores
151
- # drop active from state
152
- state.pop("_raise_error", None)
153
- state.pop("_active", None)
154
- state.pop("_finalized", None)
155
- state.pop("_map", None)
156
- return super().__setstate__(state)
157
-
158
- @staticmethod
159
- def _set_values(obj, dictionary, parent, strict: bool = True):
160
- # recursively reconstruct the object
161
- for key, value in dictionary.items():
162
- attr_key = key
163
- attr_value = value
164
- if isinstance(attr_value, dict):
165
- if parent is not None:
166
- obj._path = attr_key
167
- attr_value = Aggregator._reconstruct(attr_value, parent=parent, strict=strict)
168
- if attr_key.startswith(SPECIAL_CONSTANT):
169
- attr_key = attr_key.replace(SPECIAL_CONSTANT, "")
170
- if attr_key == "_value":
171
- try:
172
- attr_value = np.asarray(attr_value, dtype=np.float32)
173
- except Exception as e:
174
- if strict:
175
- msg = f"Could not set value of Aggregator object: {obj.path}! ERROR: {e}"
176
- UserMessage(msg)
177
- raise Exception(msg) from e
178
- obj.__setattr__(attr_key, attr_value)
179
-
180
- @staticmethod
181
- def _reconstruct(dictionary, parent=None, strict: bool = True):
182
- obj = Aggregator()
183
- obj._parent = parent
184
- if parent is not None:
185
- parent._children.append(obj)
186
- Aggregator._set_values(obj, dictionary, parent=obj, strict=strict)
187
- return obj
188
-
189
- def __str__(self) -> str:
190
- """
191
- Get the string representation of the Symbol object.
192
-
193
- Returns:
194
- str: The string representation of the Symbol object.
195
- """
196
- return str(self.entries)
197
-
198
- def _to_symbol(self, other) -> Symbol:
199
- sym = super()._to_symbol(other)
200
- res = Aggregator(sym)
201
- res._parent = self
202
- self._children.append(res)
203
- return
204
-
205
- @property
206
- def path(self) -> str:
207
- path = ""
208
- obj = self
209
- while obj is not None:
210
- if obj._path is not None:
211
- path = obj._path.replace(SPECIAL_CONSTANT, "") + "." + path
212
- obj = obj._parent
213
- return path[:-1] # remove last dot
214
-
215
- def __or__(self, other: Any) -> Any:
216
- self.add(other)
217
- return other
218
-
219
- def __ror__(self, other: Any) -> Any:
220
- self.add(other)
221
- return other
222
-
223
- def __ior__(self, other: Any) -> Any:
224
- self.add(other)
225
- return other
226
-
227
- def __len__(self) -> int:
228
- return len(self._value)
229
-
230
- @property
231
- def entries(self):
232
- return self._value
233
-
234
- @property
235
- def value(self):
236
- if self.map is not None:
237
- return np.asarray(self.map(np.asarray(self._value, dtype=np.float32)))
238
- return np.asarray(self._value, dtype=np.float32)
239
-
240
- @property
241
- def map(self):
242
- return self._map if not self.empty() else None
243
-
244
- @map.setter
245
- def map(self, value):
246
- self._set_map_recursively(value)
247
-
248
- def _set_map_recursively(self, map):
249
- self._map = map
250
- for key, value in self.__dict__.items():
251
- if isinstance(value, Aggregator) and (
252
- not key.startswith("_") or key.startswith(SPECIAL_CONSTANT)
253
- ):
254
- value.map = map
255
-
256
- def shape(self):
257
- if len(self.entries) > 0:
258
- return np.asarray(self.entries).shape
259
- return ()
260
-
261
- def serialize(self):
262
- return json.dumps(self, cls=AggregatorJSONEncoder)
263
-
264
- def save(self, path: str):
265
- with Path(path).open("w") as f:
266
- json.dump(self, f, cls=AggregatorJSONEncoder)
267
-
268
- @staticmethod
269
- def load(path: str, strict: bool = True):
270
- with Path(path).open() as f:
271
- json_ = json.load(f)
272
- return Aggregator._reconstruct(json_, strict=strict)
273
-
274
- def empty(self) -> bool:
275
- return len(self) == 0
276
-
277
- def add(self, entries):
278
- # Add entries to the aggregator
279
- if not self.active and self._finalized:
280
- UserMessage("Aggregator object is frozen!", raise_with=Exception)
281
- return
282
- try:
283
- processed_entries = self._prepare_entries(entries)
284
- if processed_entries is None:
285
- return
286
- processed_entries = self._squeeze_entries(processed_entries)
287
- self.entries.append(processed_entries)
288
- except Exception as e:
289
- msg = f"Could not add entries to Aggregator object! Please verify type or original error: {e}"
290
- if self._raise_error:
291
- UserMessage(msg)
292
- raise Exception(msg) from e
293
- UserMessage(msg)
294
-
295
- def _prepare_entries(self, entries):
296
- valid_types = (
297
- tuple,
298
- list,
299
- np.float32,
300
- np.float64,
301
- np.ndarray,
302
- torch.Tensor,
303
- int,
304
- float,
305
- bool,
306
- str,
307
- Symbol,
308
- )
309
- assert isinstance(entries, valid_types), (
310
- f"Entries must be a tuple, list, numpy array, torch tensor, integer, float, boolean, string, or Symbol! Got: {type(entries)}"
311
- )
312
- if isinstance(entries, torch.Tensor):
313
- return entries.detach().cpu().numpy().astype(np.float32)
314
- if isinstance(entries, (tuple, list)):
315
- return np.asarray(entries, dtype=np.float32)
316
- if isinstance(entries, bool):
317
- return int(entries)
318
- if isinstance(entries, str):
319
- return Symbol(entries).embedding.astype(np.float32)
320
- if isinstance(entries, Symbol):
321
- # Use this to avoid recursion on map setter
322
- self.add(entries._value)
323
- return None
324
- if isinstance(entries, Aggregator):
325
- self.add(entries.get())
326
- return None
327
- return entries
328
-
329
- def _squeeze_entries(self, entries):
330
- if isinstance(entries, (np.ndarray, np.float32)):
331
- return entries.squeeze()
332
- return entries
333
-
334
- def keys(self):
335
- # Get all key names of items that have the SPECIAL_CONSTANT prefix
336
- return [
337
- key.replace(SPECIAL_CONSTANT, "")
338
- for key in self.__dict__
339
- if not key.startswith("_") and key.replace(SPECIAL_CONSTANT, "") not in EXCLUDE_LIST
340
- ]
341
-
342
- @property
343
- def active(self):
344
- # Get the active status of the aggregator
345
- return self._active
346
-
347
- @active.setter
348
- def active(self, value):
349
- # Set the active status of the aggregator
350
- assert isinstance(value, bool), f"Active status must be a boolean! Got: {type(value)}"
351
- self._active = value
352
-
353
- @property
354
- def finalized(self):
355
- # Get the finalized status of the aggregator
356
- return self._finalized
357
-
358
- @finalized.setter
359
- def finalized(self, value):
360
- # Set the finalized status of the aggregator
361
- assert isinstance(value, bool), f"Finalized status must be a boolean! Got: {type(value)}"
362
- self._finalized = value
363
-
364
- def finalize(self):
365
- # Finalizes the dynamic creation of the aggregators and freezes the object to prevent further changes
366
- self._active = False
367
- self._finalized = True
368
-
369
- def raise_exception(name, value):
370
- if name == "map":
371
- self.__setattr__(name, value)
372
- else:
373
- UserMessage("Aggregator object is frozen!", raise_with=Exception)
374
-
375
- self.__setattr__ = raise_exception
376
-
377
- def get_attribute(*args, **kwargs):
378
- return self.__dict__.get(*args, **kwargs)
379
-
380
- self.__getattr__ = get_attribute
381
- # Do the same recursively for all properties of type Aggregator
382
- for key, value in self.__dict__.items():
383
- if isinstance(value, Aggregator) and (
384
- not key.startswith("_") or key.startswith(SPECIAL_CONSTANT)
385
- ):
386
- value.finalize()
387
-
388
- def get(self, *args, **kwargs):
389
- if self._map is not None:
390
- return self._map(self.entries, *args, **kwargs)
391
- # Get the entries of the aggregator
392
- return self.entries
393
-
394
- def clear(self):
395
- # Clear the entries of the aggregator
396
- if self._finalized:
397
- UserMessage("Aggregator object is frozen!", raise_with=Exception)
398
- self._value = []
399
-
400
- def sum(self, axis=0):
401
- # Get the sum of the entries of the aggregator
402
- return np.sum(self.entries, axis=axis)
403
-
404
- def mean(self, axis=0):
405
- # Get the mean of the entries of the aggregator
406
- return np.mean(self.entries, axis=axis)
407
-
408
- def median(self, axis=0):
409
- # Get the median of the entries of the aggregator
410
- return np.median(self.entries, axis=axis)
411
-
412
- def var(self, axis=0):
413
- # Get the variance of the entries of the aggregator
414
- return np.var(self.entries, axis=axis)
415
-
416
- def cov(self, rowvar=False):
417
- # Get the covariance of the entries of the aggregator
418
- return np.cov(self.entries, rowvar=rowvar)
419
-
420
- def moment(self, moment=2, axis=0):
421
- # Get the moment of the entries of the aggregator
422
- return np.mean(np.power(self.entries, moment), axis=axis)
423
-
424
- def std(self, axis=0):
425
- # Get the standard deviation of the entries of the aggregator
426
- return np.std(self.entries, axis=axis)
427
-
428
- def min(self, axis=0):
429
- # Get the minimum of the entries of the aggregator
430
- return np.min(self.entries, axis=axis)
431
-
432
- def max(self, axis=0):
433
- # Get the maximum of the entries of the aggregator
434
- return np.max(self.entries, axis=axis)
symai/extended/crawler.py DELETED
@@ -1,21 +0,0 @@
1
- from typing import List
2
-
3
- from ..components import Clean, Sequence, Stream
4
- from ..symbol import Expression, Symbol
5
- from ..interfaces import Interface
6
-
7
-
8
- class Crawler(Expression):
9
- def __init__(self, filters: List[Expression] = [], **kwargs):
10
- super().__init__(**kwargs)
11
- filters = filters if isinstance(filters, List) or isinstance(filters, tuple) else [filters]
12
- self.crawler = Interface('selenium')
13
- self.data_stream = Stream(Sequence(
14
- Clean(),
15
- *filters
16
- ))
17
-
18
- def forward(self, url: str, pattern='www', **kwargs) -> Symbol:
19
- res = self.crawler(url=url, pattern=pattern, **kwargs)
20
- vals = list(self.data_stream(res, **kwargs))
21
- return Symbol(vals)
@@ -1,18 +0,0 @@
1
- from ... import core
2
- from ...symbol import Expression
3
- from ...backend.engines.crawler.engine_selenium import SeleniumResult
4
-
5
-
6
- class selenium(Expression):
7
- def __init__(self, *args, **kwargs):
8
- super().__init__(*args, **kwargs)
9
- self.name = self.__class__.__name__
10
-
11
- def __call__(self, url: str, pattern: str = '', **kwargs) -> SeleniumResult:
12
- url = str(url)
13
- url = url if url.startswith('http') or url.startswith('file://') else 'https://' + url
14
- pattern = str(pattern)
15
- @core.fetch(url=url, pattern=pattern, **kwargs)
16
- def _func(_) -> SeleniumResult:
17
- pass
18
- return _func(self)
@@ -1,21 +0,0 @@
1
- from ... import core
2
- from ...symbol import Expression
3
- from ...backend.engines.index.engine_vectordb import VectorDBResult, VectorDBIndexEngine
4
-
5
-
6
- class vectordb(Expression):
7
- def __init__(self, index_name = VectorDBIndexEngine._default_index_name, *args, **kwargs):
8
- super().__init__(*args, **kwargs)
9
- self.index_name = index_name
10
- self.name = self.__class__.__name__
11
-
12
- def __call__(self, stmt: str, operation: str = "search", index_name = None, **kwargs) -> VectorDBResult:
13
- stmt = self._to_symbol(stmt)
14
- index = self.index_name if index_name is None else index_name
15
- if operation == "search":
16
- return self.get(query=stmt.embedding, index_name=index, ori_query=stmt.value, **kwargs)
17
- elif operation == "add":
18
- return self.add(doc=stmt.zip(), index_name=index, **kwargs)
19
- elif operation == "config":
20
- return self.index(path=stmt.value, index_name=index, **kwargs)
21
- raise NotImplementedError("Operation not supported")
@@ -1,3 +0,0 @@
1
- from .persona import Persona
2
- from .builder import PersonaBuilder
3
- from .dialogue import Dialogue
@@ -1,105 +0,0 @@
1
- from ...components import Function
2
- from ...symbol import Expression, Symbol
3
- from .persona import Persona # keep this import for reflection to work
4
-
5
-
6
- PERSONA_BUILDER_DESCRIPTION = """[Task]
7
- Create a detailed persona description about a character called {name}, which is as {description}.
8
- Be very detailed and specific about the persona, and include his visual appearance, character description, personality, background, quirks, and other relevant information. Add also past job and education history.
9
- Add also a description how the persona usually interacts with other people, and how he speaks and behaves in general.
10
- Include also friends and family and how the persona interacts with them. Provide at least two names of his family or close friends and a one sentence description of them. {relation_description}
11
-
12
- Do not create any other markdown format than the one provided in the exemplary output structure.
13
-
14
- [Exemplary Output Structure] // add and vary the information or bullets if needed
15
- Persona Description >>>
16
- Name: ...
17
- Age: ...
18
- Height: ...
19
- Build: ...
20
- Hair Color: ...
21
- Eye Color: ...
22
- Fashion Sense: ...
23
- ... # other relevant information
24
-
25
- Character Description:
26
- ...
27
-
28
- Personality:
29
- ...
30
-
31
- Background:
32
- ...
33
-
34
- Education:
35
- ...
36
-
37
- Quirks:
38
- ...
39
-
40
- Interactions with Other People:
41
- ...
42
-
43
- Friends and Family:
44
- ...
45
-
46
- Past Job and Education History:
47
- ...
48
-
49
- Additional Information:
50
- ...
51
- <<<
52
- """
53
-
54
-
55
- PERSONA_TEMPLATE = """
56
- def run():
57
- from symai.extended.personas import Persona
58
-
59
- CURRENT_PERSONA_DESCRIPTION = '''{persona_description}'''
60
-
61
- class PersonaClass(Persona):
62
- @property
63
- def static_context(self) -> str:
64
- return super().static_context + CURRENT_PERSONA_DESCRIPTION
65
-
66
- def __init__(self, *args, **kwargs):
67
- super().__init__(*args, **kwargs)
68
- self.bot_tag = f'{name}::'
69
- self.user_tag = 'Other Person::'
70
- self.sym_return_type = PersonaClass
71
-
72
- def bio(self) -> str:
73
- return CURRENT_PERSONA_DESCRIPTION
74
-
75
- return PersonaClass
76
- """
77
-
78
-
79
- class PersonaBuilder(Expression):
80
- def __init__(self, **kwargs):
81
- super().__init__(**kwargs)
82
- self.func = Function(PERSONA_BUILDER_DESCRIPTION)
83
-
84
- def forward(self, name: str, description: str, relation: str = '', resume: Symbol = None, **kwargs) -> str:
85
- if relation:
86
- relation = f'The persona is related to {relation}.'
87
- self.func.format(name=name, description=description, relation_description=relation, **kwargs)
88
- if resume:
89
- sym = '[PERSONA GENERATION GUIDANCE based on RESUME]\n' @ self._to_symbol(resume)
90
- description = self.func(payload=sym)
91
- else:
92
- description = self.func()
93
- # extract full name
94
- full_name = None
95
- for line in str(description).split('\n'):
96
- if 'Name:' in line:
97
- full_name = line.split('Name:')[-1].strip()
98
- break
99
- class_node = PERSONA_TEMPLATE.format(persona_description=description, name=full_name)
100
- # Create the class dynamically and return an instance.
101
- globals_ = globals().copy()
102
- locals_ = {}
103
- exec(class_node, globals_, locals_)
104
- PersonaClass = locals_['run']()
105
- return PersonaClass