symbolicai 1.0.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.
- symai/__init__.py +198 -134
- symai/backend/base.py +51 -51
- symai/backend/engines/drawing/engine_bfl.py +33 -33
- symai/backend/engines/drawing/engine_gpt_image.py +4 -10
- symai/backend/engines/embedding/engine_llama_cpp.py +50 -35
- symai/backend/engines/embedding/engine_openai.py +22 -16
- symai/backend/engines/execute/engine_python.py +16 -16
- symai/backend/engines/files/engine_io.py +51 -49
- symai/backend/engines/imagecaptioning/engine_blip2.py +27 -23
- symai/backend/engines/imagecaptioning/engine_llavacpp_client.py +53 -46
- symai/backend/engines/index/engine_pinecone.py +116 -88
- symai/backend/engines/index/engine_qdrant.py +1011 -0
- symai/backend/engines/index/engine_vectordb.py +78 -52
- symai/backend/engines/lean/engine_lean4.py +65 -25
- symai/backend/engines/neurosymbolic/__init__.py +28 -28
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_chat.py +137 -135
- symai/backend/engines/neurosymbolic/engine_anthropic_claudeX_reasoning.py +145 -152
- symai/backend/engines/neurosymbolic/engine_cerebras.py +328 -0
- symai/backend/engines/neurosymbolic/engine_deepseekX_reasoning.py +75 -49
- symai/backend/engines/neurosymbolic/engine_google_geminiX_reasoning.py +199 -155
- symai/backend/engines/neurosymbolic/engine_groq.py +106 -72
- symai/backend/engines/neurosymbolic/engine_huggingface.py +100 -67
- symai/backend/engines/neurosymbolic/engine_llama_cpp.py +121 -93
- symai/backend/engines/neurosymbolic/engine_openai_gptX_chat.py +213 -132
- symai/backend/engines/neurosymbolic/engine_openai_gptX_reasoning.py +180 -137
- symai/backend/engines/ocr/engine_apilayer.py +18 -20
- symai/backend/engines/output/engine_stdout.py +9 -9
- symai/backend/engines/{webscraping → scrape}/engine_requests.py +25 -11
- symai/backend/engines/search/engine_openai.py +95 -83
- symai/backend/engines/search/engine_parallel.py +665 -0
- symai/backend/engines/search/engine_perplexity.py +40 -41
- symai/backend/engines/search/engine_serpapi.py +33 -28
- symai/backend/engines/speech_to_text/engine_local_whisper.py +37 -27
- symai/backend/engines/symbolic/engine_wolframalpha.py +14 -8
- symai/backend/engines/text_to_speech/engine_openai.py +15 -19
- symai/backend/engines/text_vision/engine_clip.py +34 -28
- symai/backend/engines/userinput/engine_console.py +3 -4
- symai/backend/mixin/anthropic.py +48 -40
- symai/backend/mixin/deepseek.py +4 -5
- symai/backend/mixin/google.py +5 -4
- symai/backend/mixin/groq.py +2 -4
- symai/backend/mixin/openai.py +132 -110
- symai/backend/settings.py +14 -14
- symai/chat.py +164 -94
- symai/collect/dynamic.py +13 -11
- symai/collect/pipeline.py +39 -31
- symai/collect/stats.py +109 -69
- symai/components.py +556 -238
- symai/constraints.py +14 -5
- symai/core.py +1495 -1210
- symai/core_ext.py +55 -50
- symai/endpoints/api.py +113 -58
- symai/extended/api_builder.py +22 -17
- symai/extended/arxiv_pdf_parser.py +13 -5
- symai/extended/bibtex_parser.py +8 -4
- symai/extended/conversation.py +88 -69
- symai/extended/document.py +40 -27
- symai/extended/file_merger.py +45 -7
- symai/extended/graph.py +38 -24
- symai/extended/html_style_template.py +17 -11
- symai/extended/interfaces/blip_2.py +1 -1
- symai/extended/interfaces/clip.py +4 -2
- symai/extended/interfaces/console.py +5 -3
- symai/extended/interfaces/dall_e.py +3 -1
- symai/extended/interfaces/file.py +2 -0
- symai/extended/interfaces/flux.py +3 -1
- symai/extended/interfaces/gpt_image.py +15 -6
- symai/extended/interfaces/input.py +2 -1
- symai/extended/interfaces/llava.py +1 -1
- symai/extended/interfaces/{naive_webscraping.py → naive_scrape.py} +3 -2
- symai/extended/interfaces/naive_vectordb.py +2 -2
- symai/extended/interfaces/ocr.py +4 -2
- symai/extended/interfaces/openai_search.py +2 -0
- symai/extended/interfaces/parallel.py +30 -0
- symai/extended/interfaces/perplexity.py +2 -0
- symai/extended/interfaces/pinecone.py +6 -4
- symai/extended/interfaces/python.py +2 -0
- symai/extended/interfaces/serpapi.py +2 -0
- symai/extended/interfaces/terminal.py +0 -1
- symai/extended/interfaces/tts.py +2 -1
- symai/extended/interfaces/whisper.py +2 -1
- symai/extended/interfaces/wolframalpha.py +1 -0
- symai/extended/metrics/__init__.py +1 -1
- symai/extended/metrics/similarity.py +5 -2
- symai/extended/os_command.py +31 -22
- symai/extended/packages/symdev.py +39 -34
- symai/extended/packages/sympkg.py +30 -27
- symai/extended/packages/symrun.py +46 -35
- symai/extended/repo_cloner.py +10 -9
- symai/extended/seo_query_optimizer.py +15 -12
- symai/extended/solver.py +104 -76
- symai/extended/summarizer.py +8 -7
- symai/extended/taypan_interpreter.py +10 -9
- symai/extended/vectordb.py +28 -15
- symai/formatter/formatter.py +39 -31
- symai/formatter/regex.py +46 -44
- symai/functional.py +184 -86
- symai/imports.py +85 -51
- symai/interfaces.py +1 -1
- symai/memory.py +33 -24
- symai/menu/screen.py +28 -19
- symai/misc/console.py +27 -27
- symai/misc/loader.py +4 -3
- symai/models/base.py +147 -76
- symai/models/errors.py +1 -1
- symai/ops/__init__.py +1 -1
- symai/ops/measures.py +17 -14
- symai/ops/primitives.py +933 -635
- symai/post_processors.py +28 -24
- symai/pre_processors.py +58 -52
- symai/processor.py +15 -9
- symai/prompts.py +714 -649
- symai/server/huggingface_server.py +115 -32
- symai/server/llama_cpp_server.py +14 -6
- symai/server/qdrant_server.py +206 -0
- symai/shell.py +98 -39
- symai/shellsv.py +307 -223
- symai/strategy.py +135 -81
- symai/symbol.py +276 -225
- symai/utils.py +62 -46
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/METADATA +19 -9
- symbolicai-1.1.0.dist-info/RECORD +168 -0
- symbolicai-1.0.0.dist-info/RECORD +0 -163
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/WHEEL +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/entry_points.txt +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {symbolicai-1.0.0.dist-info → symbolicai-1.1.0.dist-info}/top_level.txt +0 -0
symai/backend/base.py
CHANGED
|
@@ -7,48 +7,47 @@ from ..collect import CollectionRepository, rec_serialize
|
|
|
7
7
|
from ..utils import UserMessage
|
|
8
8
|
from .settings import HOME_PATH
|
|
9
9
|
|
|
10
|
-
ENGINE_UNREGISTERED =
|
|
10
|
+
ENGINE_UNREGISTERED = "<UNREGISTERED/>"
|
|
11
11
|
|
|
12
12
|
COLLECTION_LOGGING_ENGINES = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
"GPTXChatEngine",
|
|
14
|
+
"GPTXCompletionEngine",
|
|
15
|
+
"SerpApiEngine",
|
|
16
|
+
"WolframAlphaEngine",
|
|
17
|
+
"SeleniumEngine",
|
|
18
|
+
"OCREngine",
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
class Engine(ABC):
|
|
22
23
|
def __init__(self) -> None:
|
|
23
24
|
super().__init__()
|
|
24
|
-
self.verbose
|
|
25
|
-
self.logging
|
|
26
|
-
self.log_level
|
|
25
|
+
self.verbose = False
|
|
26
|
+
self.logging = False
|
|
27
|
+
self.log_level = logging.DEBUG
|
|
27
28
|
self.time_clock = False
|
|
28
29
|
self.collection = CollectionRepository()
|
|
29
30
|
self.collection.connect()
|
|
30
31
|
# create formatter
|
|
31
|
-
__root_dir__
|
|
32
|
+
__root_dir__ = HOME_PATH
|
|
32
33
|
__root_dir__.mkdir(parents=True, exist_ok=True)
|
|
33
34
|
__file_path__ = __root_dir__ / "engine.log"
|
|
34
|
-
logging.basicConfig(
|
|
35
|
-
|
|
35
|
+
logging.basicConfig(
|
|
36
|
+
filename=__file_path__,
|
|
37
|
+
filemode="a",
|
|
38
|
+
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
|
39
|
+
)
|
|
40
|
+
self.logger = logging.getLogger()
|
|
36
41
|
self.logger.setLevel(logging.DEBUG)
|
|
37
42
|
# logging to console
|
|
38
|
-
stream
|
|
39
|
-
streamformat
|
|
43
|
+
stream = logging.StreamHandler()
|
|
44
|
+
streamformat = logging.Formatter("%(asctime)s %(message)s")
|
|
40
45
|
stream.setLevel(logging.INFO)
|
|
41
46
|
stream.setFormatter(streamformat)
|
|
42
47
|
self.logger.addHandler(stream)
|
|
43
48
|
|
|
44
49
|
def __call__(self, argument: Any) -> tuple[list[str], dict]:
|
|
45
|
-
log = {
|
|
46
|
-
'Input': {
|
|
47
|
-
'self': self,
|
|
48
|
-
'args': argument.args,
|
|
49
|
-
**argument.kwargs
|
|
50
|
-
}
|
|
51
|
-
}
|
|
50
|
+
log = {"Input": {"self": self, "args": argument.args, **argument.kwargs}}
|
|
52
51
|
start_time = time.time()
|
|
53
52
|
|
|
54
53
|
self._trigger_input_handlers(argument)
|
|
@@ -56,12 +55,12 @@ class Engine(ABC):
|
|
|
56
55
|
res, metadata = self.forward(argument)
|
|
57
56
|
|
|
58
57
|
req_time = time.time() - start_time
|
|
59
|
-
metadata[
|
|
58
|
+
metadata["time"] = req_time
|
|
60
59
|
if self.time_clock:
|
|
61
60
|
UserMessage(f"{argument.prop.func}: {req_time} sec")
|
|
62
|
-
log[
|
|
61
|
+
log["Output"] = res
|
|
63
62
|
if self.verbose:
|
|
64
|
-
view
|
|
63
|
+
view = {k: v for k, v in list(log["Input"].items()) if k != "self"}
|
|
65
64
|
input_ = f"{str(log['Input']['self'])[:50]}, {argument.prop.func!s}, {view!s}"
|
|
66
65
|
UserMessage(f"{input_[:150]} {str(log['Output'])[:100]}")
|
|
67
66
|
if self.logging:
|
|
@@ -74,9 +73,9 @@ class Engine(ABC):
|
|
|
74
73
|
return res, metadata
|
|
75
74
|
|
|
76
75
|
def _trigger_input_handlers(self, argument: Any) -> None:
|
|
77
|
-
instance_metadata = getattr(argument.prop.instance,
|
|
76
|
+
instance_metadata = getattr(argument.prop.instance, "_metadata", None)
|
|
78
77
|
if instance_metadata is not None:
|
|
79
|
-
input_handler = getattr(instance_metadata,
|
|
78
|
+
input_handler = getattr(instance_metadata, "input_handler", None)
|
|
80
79
|
if input_handler is not None:
|
|
81
80
|
input_handler((argument.prop.processed_input, argument))
|
|
82
81
|
argument_handler = argument.prop.input_handler
|
|
@@ -84,9 +83,9 @@ class Engine(ABC):
|
|
|
84
83
|
argument_handler((argument.prop.processed_input, argument))
|
|
85
84
|
|
|
86
85
|
def _trigger_output_handlers(self, argument: Any, result: Any, metadata: dict | None) -> None:
|
|
87
|
-
instance_metadata = getattr(argument.prop.instance,
|
|
86
|
+
instance_metadata = getattr(argument.prop.instance, "_metadata", None)
|
|
88
87
|
if instance_metadata is not None:
|
|
89
|
-
output_handler = getattr(instance_metadata,
|
|
88
|
+
output_handler = getattr(instance_metadata, "output_handler", None)
|
|
90
89
|
if output_handler:
|
|
91
90
|
output_handler(result)
|
|
92
91
|
argument_handler = argument.prop.output_handler
|
|
@@ -95,13 +94,13 @@ class Engine(ABC):
|
|
|
95
94
|
|
|
96
95
|
def _record_collection_entry(self, argument: Any, metadata: dict, req_time: float) -> None:
|
|
97
96
|
self.collection.add(
|
|
98
|
-
forward={
|
|
97
|
+
forward={"args": rec_serialize(argument.args), "kwds": rec_serialize(argument.kwargs)},
|
|
99
98
|
engine=str(self),
|
|
100
99
|
metadata={
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
100
|
+
"time": req_time,
|
|
101
|
+
"data": rec_serialize(metadata),
|
|
102
|
+
"argument": rec_serialize(argument),
|
|
103
|
+
},
|
|
105
104
|
)
|
|
106
105
|
|
|
107
106
|
def id(self) -> str:
|
|
@@ -109,17 +108,18 @@ class Engine(ABC):
|
|
|
109
108
|
|
|
110
109
|
def preview(self, argument):
|
|
111
110
|
# Used here to avoid backend.base <-> symbol circular import.
|
|
112
|
-
from ..symbol import (
|
|
111
|
+
from ..symbol import ( # noqa
|
|
113
112
|
Symbol,
|
|
114
113
|
)
|
|
114
|
+
|
|
115
115
|
class Preview(Symbol):
|
|
116
116
|
def __repr__(self) -> str:
|
|
117
|
-
|
|
117
|
+
"""
|
|
118
118
|
Get the representation of the Symbol object as a string.
|
|
119
119
|
|
|
120
120
|
Returns:
|
|
121
121
|
str: The representation of the Symbol object.
|
|
122
|
-
|
|
122
|
+
"""
|
|
123
123
|
return str(self.value.prop.prepared_input)
|
|
124
124
|
|
|
125
125
|
def prepared_input(self):
|
|
@@ -136,29 +136,29 @@ class Engine(ABC):
|
|
|
136
136
|
raise NotImplementedError
|
|
137
137
|
|
|
138
138
|
def command(self, *_args, **kwargs):
|
|
139
|
-
if kwargs.get(
|
|
140
|
-
self.verbose = kwargs[
|
|
141
|
-
if kwargs.get(
|
|
142
|
-
self.logging = kwargs[
|
|
143
|
-
if kwargs.get(
|
|
144
|
-
self.log_level = kwargs[
|
|
145
|
-
if kwargs.get(
|
|
146
|
-
self.time_clock = kwargs[
|
|
139
|
+
if kwargs.get("verbose"):
|
|
140
|
+
self.verbose = kwargs["verbose"]
|
|
141
|
+
if kwargs.get("logging"):
|
|
142
|
+
self.logging = kwargs["logging"]
|
|
143
|
+
if kwargs.get("log_level"):
|
|
144
|
+
self.log_level = kwargs["log_level"]
|
|
145
|
+
if kwargs.get("time_clock"):
|
|
146
|
+
self.time_clock = kwargs["time_clock"]
|
|
147
147
|
|
|
148
148
|
def __str__(self) -> str:
|
|
149
149
|
return self.__class__.__name__
|
|
150
150
|
|
|
151
151
|
def __repr__(self) -> str:
|
|
152
|
-
|
|
152
|
+
"""
|
|
153
153
|
Get the representation of the Symbol object as a string.
|
|
154
154
|
|
|
155
155
|
Returns:
|
|
156
156
|
str: The representation of the Symbol object.
|
|
157
|
-
|
|
157
|
+
"""
|
|
158
158
|
# class with full path
|
|
159
|
-
class_ = self.__class__.__module__ +
|
|
160
|
-
hex_
|
|
161
|
-
return f
|
|
159
|
+
class_ = self.__class__.__module__ + "." + self.__class__.__name__
|
|
160
|
+
hex_ = hex(id(self))
|
|
161
|
+
return f"<class {class_} at {hex_}>"
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
class BatchEngine(Engine):
|
|
@@ -196,7 +196,7 @@ class BatchEngine(Engine):
|
|
|
196
196
|
return_list = []
|
|
197
197
|
for arg, result, metadata in zip(arguments, results, metadata_list, strict=False):
|
|
198
198
|
if metadata is not None:
|
|
199
|
-
metadata[
|
|
199
|
+
metadata["time"] = total_time / len(arguments)
|
|
200
200
|
|
|
201
201
|
self._trigger_output_handlers(arg, result, metadata)
|
|
202
202
|
return_list.append((result, metadata))
|
|
@@ -22,7 +22,7 @@ class FluxResult(Result):
|
|
|
22
22
|
# unpack the result
|
|
23
23
|
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
|
|
24
24
|
path = tmp_file.name
|
|
25
|
-
url = value.get(
|
|
25
|
+
url = value.get("result").get("sample")
|
|
26
26
|
request = requests.get(url, allow_redirects=True)
|
|
27
27
|
request.raise_for_status()
|
|
28
28
|
with Path(path).open("wb") as f:
|
|
@@ -34,57 +34,57 @@ class DrawingEngine(Engine):
|
|
|
34
34
|
def __init__(self, api_key: str | None = None, model: str | None = None):
|
|
35
35
|
super().__init__()
|
|
36
36
|
self.config = SYMAI_CONFIG
|
|
37
|
-
self.api_key = self.config[
|
|
38
|
-
self.model = self.config[
|
|
37
|
+
self.api_key = self.config["DRAWING_ENGINE_API_KEY"] if api_key is None else api_key
|
|
38
|
+
self.model = self.config["DRAWING_ENGINE_MODEL"] if model is None else model
|
|
39
39
|
self.name = self.__class__.__name__
|
|
40
40
|
|
|
41
41
|
def id(self) -> str:
|
|
42
|
-
if
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if self.config["DRAWING_ENGINE_API_KEY"] and self.config["DRAWING_ENGINE_MODEL"].startswith(
|
|
43
|
+
"flux"
|
|
44
|
+
):
|
|
45
|
+
return "drawing"
|
|
46
|
+
return super().id() # default to unregistered
|
|
45
47
|
|
|
46
48
|
def command(self, *args, **kwargs):
|
|
47
49
|
super().command(*args, **kwargs)
|
|
48
|
-
if
|
|
49
|
-
self.api_key = kwargs[
|
|
50
|
-
if
|
|
51
|
-
self.model = kwargs[
|
|
50
|
+
if "DRAWING_ENGINE_API_KEY" in kwargs:
|
|
51
|
+
self.api_key = kwargs["DRAWING_ENGINE_API_KEY"]
|
|
52
|
+
if "DRAWING_ENGINE_MODEL" in kwargs:
|
|
53
|
+
self.model = kwargs["DRAWING_ENGINE_MODEL"]
|
|
52
54
|
|
|
53
55
|
def forward(self, argument):
|
|
54
56
|
prompt = argument.prop.prepared_input
|
|
55
57
|
kwargs = argument.kwargs
|
|
56
|
-
width = kwargs.get(
|
|
57
|
-
height = kwargs.get(
|
|
58
|
-
steps = kwargs.get(
|
|
59
|
-
seed = kwargs.get(
|
|
60
|
-
guidance = kwargs.get(
|
|
61
|
-
safety_tolerance = kwargs.get(
|
|
62
|
-
except_remedy = kwargs.get(
|
|
58
|
+
width = kwargs.get("width", 1024)
|
|
59
|
+
height = kwargs.get("height", 768)
|
|
60
|
+
steps = kwargs.get("steps", 40)
|
|
61
|
+
seed = kwargs.get("seed", None)
|
|
62
|
+
guidance = kwargs.get("guidance", None)
|
|
63
|
+
safety_tolerance = kwargs.get("safety_tolerance", 2)
|
|
64
|
+
except_remedy = kwargs.get("except_remedy", None)
|
|
63
65
|
|
|
64
66
|
headers = {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
"accept": "application/json",
|
|
68
|
+
"x-key": self.api_key,
|
|
69
|
+
"Content-Type": "application/json",
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
payload = {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
"prompt": prompt,
|
|
74
|
+
"width": width,
|
|
75
|
+
"height": height,
|
|
76
|
+
"num_inference_steps": steps,
|
|
77
|
+
"guidance_scale": guidance,
|
|
78
|
+
"seed": seed,
|
|
79
|
+
"safety_tolerance": safety_tolerance,
|
|
78
80
|
}
|
|
79
81
|
# drop any None values so Flux API won't return 500
|
|
80
82
|
payload = {k: v for k, v in payload.items() if v is not None}
|
|
81
83
|
|
|
82
|
-
if kwargs.get(
|
|
84
|
+
if kwargs.get("operation") == "create":
|
|
83
85
|
try:
|
|
84
86
|
response = requests.post(
|
|
85
|
-
f
|
|
86
|
-
headers=headers,
|
|
87
|
-
json=payload
|
|
87
|
+
f"https://api.us1.bfl.ai/v1/{self.model}", headers=headers, json=payload
|
|
88
88
|
)
|
|
89
89
|
# fail early on HTTP errors
|
|
90
90
|
response.raise_for_status()
|
|
@@ -100,9 +100,9 @@ class DrawingEngine(Engine):
|
|
|
100
100
|
time.sleep(5)
|
|
101
101
|
|
|
102
102
|
result = requests.get(
|
|
103
|
-
|
|
103
|
+
"https://api.us1.bfl.ai/v1/get_result",
|
|
104
104
|
headers=headers,
|
|
105
|
-
params={
|
|
105
|
+
params={"id": request_id},
|
|
106
106
|
)
|
|
107
107
|
|
|
108
108
|
result.raise_for_status()
|
|
@@ -26,6 +26,7 @@ class GPTImageResult(Result):
|
|
|
26
26
|
Exposes .value as the raw response and ._value as the
|
|
27
27
|
first URL or decoded b64 image string.
|
|
28
28
|
"""
|
|
29
|
+
|
|
29
30
|
def __init__(self, value, **kwargs):
|
|
30
31
|
super().__init__(value, **kwargs)
|
|
31
32
|
imgs = []
|
|
@@ -53,6 +54,7 @@ class GPTImageEngine(Engine):
|
|
|
53
54
|
supporting gpt-image-1, dall-e-2, dall-e-3,
|
|
54
55
|
with all the extra parameters (background, moderation, etc).
|
|
55
56
|
"""
|
|
57
|
+
|
|
56
58
|
def __init__(
|
|
57
59
|
self,
|
|
58
60
|
api_key: str | None = None,
|
|
@@ -61,16 +63,8 @@ class GPTImageEngine(Engine):
|
|
|
61
63
|
super().__init__()
|
|
62
64
|
self.config = SYMAI_CONFIG
|
|
63
65
|
# pick up a separate config slot if you like, or fall back
|
|
64
|
-
openai.api_key = (
|
|
65
|
-
|
|
66
|
-
if api_key is None
|
|
67
|
-
else api_key
|
|
68
|
-
)
|
|
69
|
-
self.model = (
|
|
70
|
-
self.config.get("DRAWING_ENGINE_MODEL")
|
|
71
|
-
if model is None
|
|
72
|
-
else model
|
|
73
|
-
)
|
|
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
|
|
74
68
|
self.name = self.__class__.__name__
|
|
75
69
|
# quiet OpenAI's internal logger
|
|
76
70
|
log = logging.getLogger("openai")
|
|
@@ -15,58 +15,66 @@ logging.getLogger("urllib").setLevel(logging.ERROR)
|
|
|
15
15
|
logging.getLogger("httpx").setLevel(logging.ERROR)
|
|
16
16
|
logging.getLogger("httpcore").setLevel(logging.ERROR)
|
|
17
17
|
|
|
18
|
+
|
|
18
19
|
class LlamaCppEmbeddingEngine(Engine):
|
|
19
20
|
_retry_params: ClassVar[dict[str, Any]] = {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
"tries": 5,
|
|
22
|
+
"delay": 2,
|
|
23
|
+
"max_delay": 60,
|
|
24
|
+
"backoff": 2,
|
|
25
|
+
"jitter": (1, 5),
|
|
26
|
+
"graceful": True,
|
|
26
27
|
}
|
|
27
28
|
_timeout_params: ClassVar[dict[str, Any]] = {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
"read": None,
|
|
30
|
+
"connect": None,
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
def __init__(
|
|
33
|
-
self,
|
|
34
|
-
retry_params: dict = _retry_params,
|
|
35
|
-
timeout_params: dict = _timeout_params
|
|
36
|
-
):
|
|
33
|
+
def __init__(self, retry_params: dict = _retry_params, timeout_params: dict = _timeout_params):
|
|
37
34
|
super().__init__()
|
|
38
35
|
self.config = SYMAI_CONFIG
|
|
39
|
-
if self.id() !=
|
|
36
|
+
if self.id() != "embedding":
|
|
40
37
|
return
|
|
41
|
-
if not SYMSERVER_CONFIG.get(
|
|
42
|
-
UserMessage(
|
|
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
|
+
)
|
|
43
43
|
|
|
44
|
-
self.server_endpoint =
|
|
44
|
+
self.server_endpoint = (
|
|
45
|
+
f"http://{SYMSERVER_CONFIG.get('--host')}:{SYMSERVER_CONFIG.get('--port')}"
|
|
46
|
+
)
|
|
45
47
|
self.timeout_params = self._validate_timeout_params(timeout_params)
|
|
46
48
|
self.retry_params = self._validate_retry_params(retry_params)
|
|
47
49
|
self.name = self.__class__.__name__
|
|
48
50
|
|
|
49
51
|
def id(self) -> str:
|
|
50
|
-
if self.config.get(
|
|
51
|
-
|
|
52
|
+
if self.config.get("EMBEDDING_ENGINE_MODEL") and self.config.get(
|
|
53
|
+
"EMBEDDING_ENGINE_MODEL"
|
|
54
|
+
).startswith("llama"):
|
|
55
|
+
return "embedding"
|
|
52
56
|
return super().id() # default to unregistered
|
|
53
57
|
|
|
54
58
|
def command(self, *args, **kwargs):
|
|
55
59
|
super().command(*args, **kwargs)
|
|
56
|
-
if
|
|
57
|
-
self.model = kwargs[
|
|
60
|
+
if "EMBEDDING_ENGINE_MODEL" in kwargs:
|
|
61
|
+
self.model = kwargs["EMBEDDING_ENGINE_MODEL"]
|
|
58
62
|
|
|
59
63
|
def _validate_timeout_params(self, timeout_params):
|
|
60
64
|
if not isinstance(timeout_params, dict):
|
|
61
65
|
UserMessage("timeout_params must be a dictionary", raise_with=ValueError)
|
|
62
|
-
assert all(key in timeout_params for key in [
|
|
66
|
+
assert all(key in timeout_params for key in ["read", "connect"]), (
|
|
67
|
+
"Available keys: ['read', 'connect']"
|
|
68
|
+
)
|
|
63
69
|
return timeout_params
|
|
64
70
|
|
|
65
71
|
def _validate_retry_params(self, retry_params):
|
|
66
72
|
if not isinstance(retry_params, dict):
|
|
67
73
|
UserMessage("retry_params must be a dictionary", raise_with=ValueError)
|
|
68
|
-
assert all(
|
|
69
|
-
|
|
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']"
|
|
70
78
|
return retry_params
|
|
71
79
|
|
|
72
80
|
@staticmethod
|
|
@@ -86,18 +94,23 @@ 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[
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
async with (
|
|
104
|
+
aiohttp.ClientSession(timeout=timeout) as session,
|
|
105
|
+
session.post(
|
|
106
|
+
f"{self.server_endpoint}/v1/embeddings",
|
|
107
|
+
json={"content": text, "embd_normalize": embd_normalize},
|
|
108
|
+
) as res,
|
|
109
|
+
):
|
|
99
110
|
if res.status != 200:
|
|
100
|
-
UserMessage(
|
|
111
|
+
UserMessage(
|
|
112
|
+
f"Request failed with status code: {res.status}", raise_with=ValueError
|
|
113
|
+
)
|
|
101
114
|
return await res.json()
|
|
102
115
|
|
|
103
116
|
return await _make_request()
|
|
@@ -107,9 +120,9 @@ class LlamaCppEmbeddingEngine(Engine):
|
|
|
107
120
|
kwargs = argument.kwargs
|
|
108
121
|
|
|
109
122
|
inp = prepared_input if isinstance(prepared_input, list) else [prepared_input]
|
|
110
|
-
embd_normalize = kwargs.get(
|
|
123
|
+
embd_normalize = kwargs.get("embd_normalize", -1) # -1 = no normalization
|
|
111
124
|
|
|
112
|
-
new_dim = kwargs.get(
|
|
125
|
+
new_dim = kwargs.get("new_dim")
|
|
113
126
|
if new_dim:
|
|
114
127
|
UserMessage("new_dim is not yet supported", raise_with=NotImplementedError)
|
|
115
128
|
|
|
@@ -122,10 +135,12 @@ class LlamaCppEmbeddingEngine(Engine):
|
|
|
122
135
|
UserMessage(f"Request failed with error: {e!s}", raise_with=ValueError)
|
|
123
136
|
|
|
124
137
|
output = [r["embedding"] for r in res] if res is not None else None # B x 1 x D
|
|
125
|
-
metadata = {
|
|
138
|
+
metadata = {"raw_output": res}
|
|
126
139
|
|
|
127
140
|
return [output], metadata
|
|
128
141
|
|
|
129
142
|
def prepare(self, argument):
|
|
130
|
-
assert not argument.prop.processed_input,
|
|
143
|
+
assert not argument.prop.processed_input, (
|
|
144
|
+
"LlamaCppEmbeddingEngine does not support processed_input."
|
|
145
|
+
)
|
|
131
146
|
argument.prop.prepared_input = argument.prop.entries
|
|
@@ -17,28 +17,30 @@ logging.getLogger("httpcore").setLevel(logging.ERROR)
|
|
|
17
17
|
class EmbeddingEngine(Engine, OpenAIMixin):
|
|
18
18
|
def __init__(self, api_key: str | None = None, model: str | None = None):
|
|
19
19
|
super().__init__()
|
|
20
|
-
logger = logging.getLogger(
|
|
20
|
+
logger = logging.getLogger("openai")
|
|
21
21
|
logger.setLevel(logging.WARNING)
|
|
22
22
|
self.config = SYMAI_CONFIG
|
|
23
|
-
if self.id() !=
|
|
24
|
-
return
|
|
25
|
-
openai.api_key = self.config[
|
|
26
|
-
self.model = self.config[
|
|
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
|
|
27
27
|
self.max_tokens = self.api_max_context_tokens()
|
|
28
28
|
self.embedding_dim = self.api_embedding_dims()
|
|
29
29
|
self.name = self.__class__.__name__
|
|
30
30
|
|
|
31
31
|
def id(self) -> str:
|
|
32
|
-
if self.config.get(
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
35
37
|
|
|
36
38
|
def command(self, *args, **kwargs):
|
|
37
39
|
super().command(*args, **kwargs)
|
|
38
|
-
if
|
|
39
|
-
openai.api_key = kwargs[
|
|
40
|
-
if
|
|
41
|
-
self.model = kwargs[
|
|
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"]
|
|
42
44
|
|
|
43
45
|
def forward(self, argument):
|
|
44
46
|
prepared_input = argument.prop.prepared_input
|
|
@@ -46,8 +48,8 @@ class EmbeddingEngine(Engine, OpenAIMixin):
|
|
|
46
48
|
kwargs = argument.kwargs
|
|
47
49
|
|
|
48
50
|
inp = prepared_input if isinstance(prepared_input, list) else [prepared_input]
|
|
49
|
-
except_remedy = kwargs.get(
|
|
50
|
-
new_dim = kwargs.get(
|
|
51
|
+
except_remedy = kwargs.get("except_remedy")
|
|
52
|
+
new_dim = kwargs.get("new_dim")
|
|
51
53
|
|
|
52
54
|
try:
|
|
53
55
|
res = openai.embeddings.create(model=self.model, input=inp)
|
|
@@ -58,7 +60,9 @@ class EmbeddingEngine(Engine, OpenAIMixin):
|
|
|
58
60
|
res = except_remedy(e, inp, callback, self, *args, **kwargs)
|
|
59
61
|
|
|
60
62
|
if new_dim:
|
|
61
|
-
mn = min(
|
|
63
|
+
mn = min(
|
|
64
|
+
new_dim, self.embedding_dim
|
|
65
|
+
) # @NOTE: new_dim should be less than or equal to the original embedding dim
|
|
62
66
|
output = [self._normalize_l2(r.embedding[:mn]) for r in res.data]
|
|
63
67
|
else:
|
|
64
68
|
output = [r.embedding for r in res.data]
|
|
@@ -68,7 +72,9 @@ class EmbeddingEngine(Engine, OpenAIMixin):
|
|
|
68
72
|
return [output], metadata
|
|
69
73
|
|
|
70
74
|
def prepare(self, argument):
|
|
71
|
-
assert not argument.prop.processed_input,
|
|
75
|
+
assert not argument.prop.processed_input, (
|
|
76
|
+
"EmbeddingEngine does not support processed_input."
|
|
77
|
+
)
|
|
72
78
|
argument.prop.prepared_input = argument.prop.entries
|
|
73
79
|
|
|
74
80
|
def _normalize_l2(self, x):
|
|
@@ -8,13 +8,13 @@ from ...base import Engine
|
|
|
8
8
|
def full_stack():
|
|
9
9
|
exc = sys.exc_info()[0]
|
|
10
10
|
stack = traceback.extract_stack()[-10:-1] # last one would be full_stack()
|
|
11
|
-
if exc is not None:
|
|
12
|
-
del stack[-1]
|
|
13
|
-
|
|
14
|
-
trc =
|
|
15
|
-
stackstr = trc +
|
|
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))
|
|
16
16
|
if exc is not None:
|
|
17
|
-
|
|
17
|
+
stackstr += " " + traceback.format_exc().lstrip(trc)
|
|
18
18
|
return stackstr
|
|
19
19
|
|
|
20
20
|
|
|
@@ -64,14 +64,14 @@ class PythonEngine(Engine):
|
|
|
64
64
|
self.name = self.__class__.__name__
|
|
65
65
|
|
|
66
66
|
def id(self) -> str:
|
|
67
|
-
return
|
|
67
|
+
return "execute"
|
|
68
68
|
|
|
69
69
|
def forward(self, argument):
|
|
70
70
|
code = argument.prop.prepared_input
|
|
71
71
|
kwargs = argument.kwargs
|
|
72
|
-
globals_ = kwargs.get(
|
|
73
|
-
locals_ = kwargs.get(
|
|
74
|
-
input_handler = kwargs.get(
|
|
72
|
+
globals_ = kwargs.get("globals", {})
|
|
73
|
+
locals_ = kwargs.get("locals", {})
|
|
74
|
+
input_handler = kwargs.get("input_handler")
|
|
75
75
|
if input_handler:
|
|
76
76
|
input_handler((code,))
|
|
77
77
|
|
|
@@ -79,18 +79,18 @@ class PythonEngine(Engine):
|
|
|
79
79
|
err = None
|
|
80
80
|
try:
|
|
81
81
|
exec(str(code), globals_, locals_)
|
|
82
|
-
rsp = {
|
|
83
|
-
if
|
|
84
|
-
rsp[
|
|
85
|
-
if
|
|
86
|
-
rsp[
|
|
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"]
|
|
87
87
|
rsp = Result(rsp)
|
|
88
88
|
except Exception as e:
|
|
89
89
|
err = e
|
|
90
90
|
raise e
|
|
91
91
|
|
|
92
92
|
metadata = {}
|
|
93
|
-
metadata[
|
|
93
|
+
metadata["error"] = None if not err else full_stack()
|
|
94
94
|
|
|
95
95
|
return [rsp], metadata
|
|
96
96
|
|