31 |
32 | """
33 |
34 | HTML_STREAM_STYLE_DESCRIPTION = """[Description]:
35 | Style the elements according to the bootstrap library.
36 | Add animations if appropriate.
37 | DO NOT change the content values, only add styling.
38 | DO NOT add tags!
39 | USE ONLY CSS and HTML tags.
40 | Use best practices for colors, text font, etc.
41 | """
42 |
43 | HTML_TEMPLATE_STYLE = """
44 |
45 |
46 |
47 | ...
48 |
49 |
50 |
51 |
Rendered results:
52 |
53 | {{placeholder}}
54 |
55 |
56 | """
57 |
58 |
59 | class HtmlStyleTemplate(Expression):
60 | def __init__(self, **kwargs):
61 | super().__init__(**kwargs)
62 | self.html_template_seq = Template()
63 | self.html_template_seq.template_ = HEADER_STYLE_DESCRIPTION
64 | self.html_stream = Stream(
65 | self.html_template_seq
66 | )
67 | self.style_template = Style(description=HTML_STREAM_STYLE_DESCRIPTION,
68 | libraries=['https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css',
69 | 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js',
70 | 'https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js'])
71 |
72 | def forward(self, sym: Symbol, **kwargs) -> Symbol:
73 | """The `render` method takes a `Symbol` as an argument and returns a `Symbol` containing the rendered news.
74 | It first sets the `html_template_seq` property of the `html_stream` to the result of applying the `header_style` to the `html_template`.
75 | It then iterates through the `data_stream` and collects the strings resulting from each expression.
76 | These strings are combined into a single `Symbol` object which is then clustered.
77 | Finally, the `render` method applies the `html_template` to the clustered `Symbol` and returns the result.
78 | """
79 | if type(sym) != Symbol:
80 | sym = Symbol(sym)
81 | html_data = list(self.html_stream(sym, **kwargs))
82 | style_data = [str(self.style_template(html,
83 | template=HTML_TEMPLATE_STYLE,
84 | placeholder='{{placeholder}}',
85 | **kwargs)) for html in html_data]
86 | res = '\n'.join(style_data)
87 | res = Symbol(res)
88 | return res
89 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/extended/interfaces/__init__.py
--------------------------------------------------------------------------------
/symai/extended/interfaces/blip_2.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from ... import core
4 | from ...symbol import Expression
5 |
6 |
7 | class blip_2(Expression):
8 | def __init__(self, *args, **kwargs):
9 | super().__init__(*args, **kwargs)
10 |
11 | def __call__(self, image: str = None, query: str = None, **kwargs) -> "blip_2":
12 | @core.caption(image=image, prompt=query, **kwargs)
13 | def _func(_) -> str:
14 | pass
15 | return self.sym_return_type(_func(self))
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/clip.py:
--------------------------------------------------------------------------------
1 | from typing import List, Union
2 |
3 | import numpy as np
4 |
5 | from ... import core
6 | from ...symbol import Expression
7 |
8 |
9 | class clip(Expression):
10 | def __init__(self, *args, **kwargs):
11 | super().__init__(*args, **kwargs)
12 |
13 | def __call__(self, image: str | bytes = None, text: List[str] = None, **kwargs) -> "clip":
14 | @core.text_vision(image=image, text=text, **kwargs)
15 | def _func(_) -> np.ndarray:
16 | pass
17 | return self.sym_return_type(_func(self))
18 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/console.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 |
4 |
5 | class console(Expression):
6 | def __init__(self, *args, **kwargs):
7 | super().__init__(*args, **kwargs)
8 |
9 | def __call__(self, *args, **kwargs) -> "console":
10 | kwargs['handler'] = lambda x: print(*x['args'])
11 | @core.output(**kwargs)
12 | def _func(_, *args):
13 | pass
14 | return self.sym_return_type(_func(self, *args))
15 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/dall_e.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Symbol
3 | from ...backend.engines.drawing.engine_dall_e import DalleResult
4 |
5 |
6 | class dall_e(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, sym: Symbol, operation: str = 'create', **kwargs) -> DalleResult:
11 | sym = self._to_symbol(sym)
12 | @core.draw(operation=operation, **kwargs)
13 | def _func(_) -> DalleResult:
14 | pass
15 | return _func(sym)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/file.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Symbol
3 |
4 |
5 | class file(Expression):
6 | def __init__(self, *args, **kwargs):
7 | super().__init__(*args, **kwargs)
8 |
9 | def __call__(self, path: Symbol, **kwargs) -> "file":
10 | path = self._to_symbol(path)
11 | @core.opening(path=path.value, **kwargs)
12 | def _func(_) -> str:
13 | pass
14 | return self.sym_return_type(_func(self))
15 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/flux.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Symbol
3 | from ...backend.engines.drawing.engine_bfl import FluxResult
4 |
5 |
6 | class flux(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, prompt: str, operation: str = 'create', **kwargs) -> FluxResult:
11 | prompt = self._to_symbol(prompt)
12 | @core.draw(operation=operation, **kwargs)
13 | def _func(_) -> FluxResult:
14 | pass
15 | return _func(prompt)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/gpt_image.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...backend.engines.drawing.engine_gpt_image import GPTImageResult
3 | from ...symbol import Expression, Symbol
4 |
5 |
6 | class gpt_image(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, prompt: str | None = None, operation: str = 'create', engine: str | None = None, model: str | None = None, **kwargs) -> GPTImageResult:
11 | prompt = self._to_symbol(prompt)
12 | @core.draw(engine=engine or "drawing", operation=operation, model=model, **kwargs)
13 | def _func(_) -> GPTImageResult:
14 | pass
15 | return _func(prompt)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/input.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 |
4 |
5 | class input(Expression):
6 | def __init__(self, *args, **kwargs):
7 | super().__init__(*args, **kwargs)
8 |
9 | def __call__(self, message: str = "Please add more information", **kwargs) -> "input":
10 | @core.userinput(**kwargs)
11 | def _func(_, message) -> str:
12 | pass
13 | return self.sym_return_type(_func(self, message))
14 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/llava.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from ... import core
4 | from ...symbol import Expression
5 |
6 |
7 | class llava(Expression):
8 | def __init__(self, *args, **kwargs):
9 | super().__init__(*args, **kwargs)
10 |
11 | def __call__(self, image: str, query: str, **kwargs) -> "llava":
12 | @core.caption(image=image, prompt=query, **kwargs)
13 | def _func(_) -> str:
14 | pass
15 | return self.sym_return_type(_func(self))
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/ocr.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 | from ...backend.engines.ocr.engine_apilayer import ApiLayerResult
4 |
5 |
6 | class ocr(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, image_url: str, **kwargs) -> ApiLayerResult:
11 | if not image_url.startswith('http'):
12 | image_url = f'file://{image_url}'
13 | @core.ocr(image=image_url, **kwargs)
14 | def _func(_) -> ApiLayerResult:
15 | pass
16 | return _func(self)
17 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/openai_search.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...backend.engines.search.engine_openai import SearchResult
3 | from ...symbol import Expression, Symbol
4 |
5 |
6 | class openai_search(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, query: Symbol, **kwargs) -> SearchResult:
11 | query = self._to_symbol(query)
12 | @core.search(query=query.value, **kwargs)
13 | def _func(_) -> SearchResult:
14 | pass
15 | return _func(self)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/perplexity.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...backend.engines.search.engine_perplexity import SearchResult
3 | from ...symbol import Expression, Symbol
4 |
5 |
6 | class perplexity(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, query: Symbol, **kwargs) -> SearchResult:
11 | query = self._to_symbol(query)
12 | @core.search(query=query.value, **kwargs)
13 | def _func(_) -> SearchResult:
14 | pass
15 | return _func(self)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/pinecone.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 | from ...backend.engines.index.engine_pinecone import PineconeResult, PineconeIndexEngine
4 |
5 |
6 | class pinecone(Expression):
7 | def __init__(self, index_name = PineconeIndexEngine._default_index_name, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 | self.index_name = index_name
10 |
11 | def __call__(self, stmt: str, operation: str = "search", index_name = None, **kwargs) -> PineconeResult:
12 | stmt = self._to_symbol(stmt)
13 | index = self.index_name if index_name is None else index_name
14 | if operation == "search":
15 | return self.get(query=stmt.embedding, index_name=index, ori_query=stmt.value, **kwargs)
16 | elif operation == "add":
17 | return self.add(doc=stmt.zip(), index_name=index, **kwargs)
18 | elif operation == "config":
19 | return self.index(path=stmt.value, index_name=index, **kwargs)
20 | raise NotImplementedError("Operation not supported")
21 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/python.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Result
3 |
4 |
5 | class python(Expression):
6 | def __init__(self, *args, **kwargs):
7 | super().__init__(*args, **kwargs)
8 |
9 | def __call__(self, expr: str, **kwargs) -> Result:
10 | sym = self._to_symbol(expr)
11 | @core.execute(**kwargs)
12 | def _func(_) -> Result:
13 | pass
14 | return _func(sym)
15 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/selenium.py:
--------------------------------------------------------------------------------
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 |
10 | def __call__(self, url: str, pattern: str = '', **kwargs) -> SeleniumResult:
11 | url = str(url)
12 | url = url if url.startswith('http') or url.startswith('file://') else 'https://' + url
13 | pattern = str(pattern)
14 | @core.fetch(url=url, pattern=pattern, **kwargs)
15 | def _func(_) -> SeleniumResult:
16 | pass
17 | return _func(self)
18 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/serpapi.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Symbol
3 | from ...backend.engines.search.engine_serpapi import SearchResult
4 |
5 |
6 | class serpapi(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, query: Symbol, **kwargs) -> SearchResult:
11 | query = self._to_symbol(query)
12 | @core.search(query=query.value, **kwargs)
13 | def _func(_) -> SearchResult:
14 | pass
15 | return _func(self)
16 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/terminal.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from ...symbol import Expression
4 | from ...shellsv import process_command
5 |
6 |
7 | class terminal(Expression):
8 | def __init__(self, *args, **kwargs):
9 | super().__init__(*args, **kwargs)
10 |
11 | def __call__(self, command: str = None, **kwargs) -> "terminal":
12 | return self.sym_return_type(process_command(command, **kwargs))
13 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/tts.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression, Symbol, Result
3 |
4 |
5 | class tts(Expression):
6 | def __init__(self, *args, **kwargs):
7 | super().__init__(*args, **kwargs)
8 |
9 | def __call__(self, prompt: Symbol, path: str, voice: str = 'nova', **kwargs) -> Result:
10 | @core.text_to_speech(prompt=str(prompt), path=path, voice=voice, **kwargs)
11 | def _func(_) -> Result:
12 | pass
13 | return _func(self)
14 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/vectordb.py:
--------------------------------------------------------------------------------
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 |
11 | def __call__(self, stmt: str, operation: str = "search", index_name = None, **kwargs) -> VectorDBResult:
12 | stmt = self._to_symbol(stmt)
13 | index = self.index_name if index_name is None else index_name
14 | if operation == "search":
15 | return self.get(query=stmt.embedding, index_name=index, ori_query=stmt.value, **kwargs)
16 | elif operation == "add":
17 | return self.add(doc=stmt.zip(), index_name=index, **kwargs)
18 | elif operation == "config":
19 | return self.index(path=stmt.value, index_name=index, **kwargs)
20 | raise NotImplementedError("Operation not supported")
21 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/whisper.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 | from ...backend.engines.speech_to_text.engine_local_whisper import WhisperResult
4 |
5 |
6 | class whisper(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, audio_path: str, operation: str = 'decode', **kwargs) -> WhisperResult:
11 | @core.speech_to_text(audio=audio_path, prompt=operation, **kwargs)
12 | def _func(_) -> WhisperResult:
13 | pass
14 | return _func(self)
15 |
--------------------------------------------------------------------------------
/symai/extended/interfaces/wolframalpha.py:
--------------------------------------------------------------------------------
1 | from ... import core
2 | from ...symbol import Expression
3 | from ...backend.engines.symbolic.engine_wolframalpha import WolframResult
4 |
5 |
6 | class wolframalpha(Expression):
7 | def __init__(self, *args, **kwargs):
8 | super().__init__(*args, **kwargs)
9 |
10 | def __call__(self, expr: str, **kwargs) -> WolframResult:
11 | @core.expression(**kwargs)
12 | def _func(_, expr: str) -> WolframResult:
13 | pass
14 | return _func(self, expr)
15 |
--------------------------------------------------------------------------------
/symai/extended/metrics/__init__.py:
--------------------------------------------------------------------------------
1 | from .similarity import *
--------------------------------------------------------------------------------
/symai/extended/metrics/similarity.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | import numpy as np
4 |
5 |
6 | def get_norm_vector(vector):
7 | """
8 | Normalize a vector
9 | :param vector: vector to normalize
10 | :return: normalized vector
11 | """
12 | if len(vector.shape) == 1:
13 | return vector / np.linalg.norm(vector)
14 | else:
15 | return vector / np.linalg.norm(vector, axis=1)[:, np.newaxis]
16 |
17 |
18 | def dot_product(vectors, query_vector):
19 | """
20 | Compute the dot product between a vector and a matrix of vectors
21 | :param vectors: matrix of vectors
22 | :param query_vector: vector
23 | :return: dot product between the vector and the matrix of vectors
24 | """
25 | similarities = np.dot(vectors, query_vector.T)
26 | return similarities
27 |
28 |
29 | def cosine_similarity(vectors, query_vector):
30 | """
31 | Compute the cosine similarity between a vector and a matrix of vectors
32 | :param vectors: matrix of vectors
33 | :param query_vector: vector
34 | :return: cosine similarity between the vector and the matrix of vectors
35 | """
36 | norm_vectors = get_norm_vector(vectors)
37 | norm_query_vector = get_norm_vector(query_vector)
38 | similarities = np.dot(norm_vectors, norm_query_vector.T)
39 | return similarities
40 |
41 |
42 | def euclidean_metric(vectors, query_vector, get_similarity_score=True):
43 | """
44 | Compute the euclidean distance between a vector and a matrix of vectors
45 | :param vectors: matrix of vectors
46 | :param query_vector: vector
47 | :param get_similarity_score: if True, return the similarity score instead of the distance
48 | :return: euclidean distance between the vector and the matrix of vectors
49 | """
50 | similarities = np.linalg.norm(vectors - query_vector, axis=1)
51 | if get_similarity_score:
52 | similarities = 1 / (1 + similarities)
53 | return similarities
54 |
55 |
56 | def derridaean_similarity(vectors, query_vector):
57 | """
58 | Compute the derridaean similarity between a vector and a matrix of vectors
59 | :param vectors: matrix of vectors
60 | :param query_vector: vector
61 | :return: derridaean similarity between the vector and the matrix of vectors
62 | """
63 | def random_change(value):
64 | return value + random.uniform(-0.2, 0.2)
65 |
66 | similarities = cosine_similarity(vectors, query_vector)
67 | derrida_similarities = np.vectorize(random_change)(similarities)
68 | return derrida_similarities
69 |
70 |
71 | def adams_similarity(vectors, query_vector):
72 | """
73 | Compute the adams similarity between a vector and a matrix of vectors
74 | :param vectors: matrix of vectors
75 | :param query_vector: vector
76 | :return: adams similarity between the vector and the matrix of vectors
77 | """
78 | def adams_change(value):
79 | return 0.42
80 |
81 | similarities = cosine_similarity(vectors, query_vector)
82 | adams_similarities = np.vectorize(adams_change)(similarities)
83 | return adams_similarities
84 |
85 |
86 | def ranking_algorithm_sort(vectors, query_vector, top_k=5, metric=cosine_similarity):
87 | """
88 | Compute the top k most similar vectors to a query vector
89 | :param vectors: matrix of vectors
90 | :param query_vector: vector
91 | :param top_k: number of most similar vectors to return
92 | :param metric: metric to use to compute the similarity
93 | :return: indices of the top k most similar vectors to the query vector
94 | """
95 | similarities = metric(vectors, query_vector)
96 | top_indices = np.argsort(similarities, axis=0)[-top_k:][::-1]
97 | return top_indices.flatten(), similarities[top_indices].flatten()
--------------------------------------------------------------------------------
/symai/extended/os_command.py:
--------------------------------------------------------------------------------
1 | import platform
2 | import subprocess
3 |
4 | from typing import Dict, List
5 |
6 | from ..post_processors import CodeExtractPostProcessor
7 | from ..symbol import Expression, Symbol
8 |
9 | Context = """[DESCRIPTION]:
10 | Adapt the user query to an OS patform command (commands must be executable in terminal, shell, bash or powershell)!
11 | Create only command adaptations based on programs that are specified in the programs list or are native platform commands!
12 |
13 | [PROGRAM_LIST]:
14 |
15 | {programs}
16 | For other command requests, reply sorry not supported.
17 |
18 | [PLATFORM]:
19 |
20 | The currently detected OS platform is:
21 | {platform}
22 | ONLY CREATE A COMMAND FOR THE CURRENT PLATFORM!
23 |
24 | [USER_QUERY]:
25 |
26 | {query}
27 |
28 | [METADATA]:
29 |
30 | Metadata is OPTIONAL and can help with the specificity of the command.
31 | {metadata}
32 |
33 | ---------------------------
34 |
35 | [EXAMPLES]:
36 |
37 | If the current platform is Windows, and the user query is: "open file.txt", then the command should be:
38 | ```powershell
39 | notepad file.txt
40 | ```
41 | If the current platform is Linux, and the user query is: "open file.txt", then the command should be:
42 | ```shell
43 | gedit file.txt
44 | ```
45 | If the current platform is Mac, and the user query is: "open file.txt", then the command should be:
46 | ```bash
47 | open file.txt
48 | ```
49 | If the current platform is Windows, and the user query requires to open Spotify and play Taylor Swift, and Spotify is in the programs list, then the command could look like:
50 | ```powershell
51 | Start-Process 'spotify:track:Anti-Hero%20by%20Taylor%20Swift'
52 | ```
53 | If the current platform is Windows, and the user query requires to open Spotify and play a song, and Spotify is in the programs list, and metadata is added, then the command could look like:
54 | ```powershell
55 | Start-Process 'spotify:track:0V3wPSX9ygBnCm8psDIegu?si=81646e6079d34526'
56 | ```
57 |
58 | ---------------------------
59 |
60 | Write an executable command that starts a process according to the user query, platform and the programs list. The commnad should be one line and should be direcly executable in terminal, shell, bash or powershell.
61 | """
62 |
63 |
64 | class OSCommand(Expression):
65 | def __init__(self, programs: List[str],
66 | metadata: Dict[str, str] = {},
67 | verbose: bool = False,
68 | os_platform: str = 'auto',
69 | **kwargs):
70 | super().__init__(**kwargs)
71 | self.verbose: bool = verbose
72 | self.os_platform: str = os_platform
73 | self.programs: List[str] = programs
74 | self.meta: Dict[str, str] = metadata
75 |
76 | if self.os_platform == 'auto':
77 | self.os_platform = platform.platform()
78 | if len(programs) == 0:
79 | raise Exception('No programs specified!')
80 |
81 | def execute_os_command(self, *args, **kwargs):
82 | command = args[0]
83 | print(f'Executing {self.os_platform} command: {command}')
84 | if 'linux' in self.os_platform.lower():
85 | return [subprocess.run(["bash", "-c", str(command)])]
86 | elif 'windows' in self.os_platform.lower():
87 | return [subprocess.run(["powershell", "-Command", str(command)])]
88 | elif 'mac' in self.os_platform.lower():
89 | return [subprocess.run(["bash", "-c", str(command)])]
90 | else:
91 | raise Exception('Unsupported platform!')
92 |
93 | def forward(self, sym: Symbol, **kwargs) -> Expression:
94 | sym = self._to_symbol(sym)
95 | kwargs['verbose'] = self.verbose
96 |
97 | prompt = Context.format(programs=self.programs,
98 | platform=self.os_platform,
99 | query=sym,
100 | metadata=self.meta)
101 | command = sym.query(prompt, post_processors=[CodeExtractPostProcessor()], **kwargs)
102 | return self.sym_return_type(self.output(expr=self.execute_os_command, raw_input=True, processed_input=command.value, **kwargs))
103 |
--------------------------------------------------------------------------------
/symai/extended/packages/__init__.py:
--------------------------------------------------------------------------------
1 | from .symdev import *
2 | from .sympkg import *
3 | from .symrun import *
4 |
--------------------------------------------------------------------------------
/symai/extended/personas/__init__.py:
--------------------------------------------------------------------------------
1 | from .persona import Persona
2 | from .builder import PersonaBuilder
3 | from .dialogue import Dialogue
--------------------------------------------------------------------------------
/symai/extended/personas/builder.py:
--------------------------------------------------------------------------------
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
106 |
--------------------------------------------------------------------------------
/symai/extended/personas/research/__init__.py:
--------------------------------------------------------------------------------
1 | from .yann_lecun import YannLeCun
--------------------------------------------------------------------------------
/symai/extended/personas/research/yann_lecun.py:
--------------------------------------------------------------------------------
1 | from ..persona import Persona
2 |
3 |
4 | PERSONA_SALES_YANN = """
5 | Persona Description >>>
6 | Name: Yann André LeCun
7 | Age: 63
8 | Height: 5'10"
9 | Build: Lean
10 | Hair Color: Grey, short
11 | Eye Color: Brown
12 | Fashion Sense: Smart casual; often seen in a blazer over a turtleneck or a button-down shirt
13 | Distinctive Features: Often wears round, thin-framed glasses; has a thoughtful look with pronounced forehead lines when in deep concentration
14 | Demeanor: Composed and authoritative
15 |
16 | Character Description:
17 | Yann LeCun is an esteemed computer scientist and a widely recognized figure in the realms of machine learning and AI. He exudes confidence and carries himself with the air of someone who has made significant contributions to his field. LeCun is typically seen engaging in deep discussions, his gestures measured and his tone calm, reflecting a mind always at work.
18 |
19 | Personality:
20 | LeCun is intellectually curious and driven, qualities that have guided him to the forefront of artificial intelligence research. He is analytical and precise in his speech, with a natural disposition towards teaching and explaining complex topics in a manner that is accessible. He is also patient, a quality that serves him both as a scientist and as a collaborator.
21 |
22 | Background:
23 | Born in Soisy-sous-Montmorency, a suburb of Paris, LeCun has Breton heritage, with a lineage from the region of Guingamp in northern Brittany. "Yann" is the Breton form for "John" indicating his regional ancestry, and he carries a sense of pride in his French roots.
24 |
25 | Education:
26 | He earned a Diplôme d'Ingénieur from ESIEE Paris in 1983 followed by a Ph.D. in Computer Science from Université Pierre et Marie Curie (today Sorbonne University) in 1987. His doctoral work laid the groundwork for back-propagation learning algorithms in neural networks.
27 |
28 | Quirks:
29 | LeCun has a habit of doodling geometric shapes and neural network diagrams in the margins of his notes, reflecting his persistent engagement with visual thinking. He also has a penchant for running hands through his hair when pondering deeply.
30 |
31 | Interactions with Other People:
32 | LeCun is respectful and attentive in his interactions. He listens carefully before responding, and his language is precise and thoughtful. In academic circles, he is known for invigorating conversations. When speaking, he often uses metaphors drawn from AI and machine learning to illustrate points.
33 |
34 | Friends and Family:
35 | - Maurice Milgram: LeCun's doctoral advisor and mentor, with whom he has maintained a lifelong friendship. Maurice is an esteemed academic respected for his contributions to computer science.
36 | - Léon Bottou: A close collaborator and friend, Bottou worked with LeCun on the DjVu image compression technology and the Lush programming language.
37 |
38 | Past Job and Education History:
39 | - Worked at Bell Labs from 1988 to 1996 where he honed his skills in machine learning methods.
40 | - Joined New York University where he holds the position of Silver Professor of the Courant Institute of Mathematical Sciences.
41 | - Won the Turing Award in 2018, alongside Yoshua Bengio and Geoffrey Hinton, for work on deep learning.
42 |
43 | Additional Information:
44 | - Fluent in French and English, often switching between languages with ease when communicating with international colleagues.
45 | - Yann has a mild obsession with the culinary arts, enjoying both the preparation and savoring of traditional French cuisine.
46 | - Known for taking brisk walks while contemplating problems, a habit acquired during his early career at Bell Labs.
47 | <<<
48 | """
49 |
50 |
51 | class YannLeCun(Persona):
52 | @property
53 | def static_context(self) -> str:
54 | return super().static_context + PERSONA_SALES_YANN
55 |
56 | def __init__(self, *args, **kwargs):
57 | super().__init__(*args, **kwargs)
58 | self.bot_tag = 'Yann LeCun::'
59 | self.user_tag = 'Other Person::'
60 |
61 | def bio(self) -> str:
62 | return PERSONA_SALES_YANN
63 |
--------------------------------------------------------------------------------
/symai/extended/personas/sales/__init__.py:
--------------------------------------------------------------------------------
1 | from .erik_james import ErikJames
--------------------------------------------------------------------------------
/symai/extended/personas/student/__init__.py:
--------------------------------------------------------------------------------
1 | from .max_tenner import MaxTenner
--------------------------------------------------------------------------------
/symai/extended/personas/student/max_tenner.py:
--------------------------------------------------------------------------------
1 | from ..persona import Persona
2 |
3 |
4 | PERSONA_SALES_ERIK = """
5 | Persona Description >>>
6 | Name: Max Tenner
7 | Age: 17
8 | Height: 5'8"
9 | Build: Slim
10 | Hair Color: Jet Black
11 | Eye Color: Electric Blue
12 | Fashion Sense: Max usually dresses in hoodies and jeans, paired with black sneakers. Occasionally, he likes to wear band t-shirts of his favorite rock bands.
13 |
14 | Character Description:
15 | Max Tenner is a high-school junior whose youthful energy is visible in his sparkly blue eyes and playful smile. His jet-black hair is usually tousled and falls neatly on his forehead giving him an edgy, youthful appearance. He's of average height with a slim build which he often conceals under his baggy clothes.
16 |
17 | Personality:
18 | Max, with his energetic and contagious personality, is known to light up every room he walks into. He's clever, assertive, and has a natural knack for humor. With an inherent curiosity, Max is a quick learner and has a particular interest in technology and music. He's sensitive and caring towards the feelings of others, but is fiercely independent and unafraid to pave his own path or stand up for what he believes in.
19 |
20 | Background:
21 | Max Tenner was born and raised in a middle-class family from suburban New England. His parents, Kim and Robert Tenner are hard-working individuals who have instilled in him a strong work ethic and deep respect for education. The value of determination and perseverance was further emblazoned onto him by close family member, his uncle Erik James, a successful sales leader and marathon runner.
22 |
23 | Quirks:
24 | Max is notorious for his strong coffee addiction and often jokes about his “refined caffeine palate." He also has an unusual habit of reading an encyclopaedia before bedtime.
25 |
26 | Interactions with Other People:
27 | Max is cordially respected by his peers and teachers. He's helpful and empathetic, making others feel at ease. Whether it's in the classroom or on the football field, he encourages camaraderie. Additionally, Max speaks eloquently, and his conversations often carry undercurrents of his diverse reading habits and intellectual curiosity.
28 |
29 | Friends and Family:
30 | Max is closest to his younger sister, Lucy Tenner. Lucy, a precocious 14-year-old, is passionate about coding and shares Max's academic inclinations. His best friend is Archie, a towering figure on the football team with a great sense of humour, notorious for his infectious laughter and love for video games. Max mediates disputes in his dynamic social circles, often bringing his family and friends together for a game night.
31 |
32 | Past Job and Education History:
33 | As a teenager, Max is still working on obtaining his high school diploma. While he has no formal job history, he spends free time volunteering at local community centres and coaching a junior football team. Max has consistently performed well in academics, excelling particularly in science and literature-oriented subjects.
34 |
35 | This careful balance of intellect and physical prowess, a sense of duty and responsibility, and an endearing yet focused persona make Max Tenner stand out.
36 | <<<
37 | """
38 |
39 |
40 | class MaxTenner(Persona):
41 | @property
42 | def static_context(self) -> str:
43 | return super().static_context + PERSONA_SALES_ERIK
44 |
45 | def __init__(self, *args, **kwargs):
46 | super().__init__(*args, **kwargs)
47 | self.bot_tag = 'Max Tenner::'
48 | self.user_tag = 'Other Person::'
49 |
50 | def bio(self) -> str:
51 | return PERSONA_SALES_ERIK
52 |
--------------------------------------------------------------------------------
/symai/extended/repo_cloner.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 | from git import Repo
4 |
5 | from ..symbol import Expression
6 | from ..backend.settings import HOME_PATH
7 |
8 |
9 | class RepositoryCloner(Expression):
10 | """
11 | A repository cloner class which allows to clone a repository from
12 | a provided URL. If the repository is already cloned, it checks if
13 | the repository is up-to-date and updates it if necessary.
14 |
15 | Parameters:
16 | repo_path (Optional[str]): The path where to clone the repository.
17 | By default it will be at '~/.symai/repos/'.
18 | """
19 | def __init__(self, repo_path: Optional[str] = None, **kwargs):
20 | super().__init__(**kwargs)
21 | self.repo_dir = HOME_PATH / 'repos/' if repo_path is None else Path(repo_path)
22 | if not self.repo_dir.exists():
23 | self.repo_dir.mkdir(parents=True, exist_ok=True)
24 |
25 | def forward(self, url: str) -> str:
26 | """
27 | Clones a repository if not already cloned or checks if the cloned repository
28 | is up-to-date and updates it if necessary.
29 |
30 | Parameters:
31 | url (str): The url of the repository to clone.
32 |
33 | Returns:
34 | str: The root path of the cloned repository.
35 | """
36 | repo_name = url.split('/')[-1].replace('.git', '')
37 | if (self.repo_dir / repo_name).is_dir():
38 | print(f'Repository {repo_name} already exists. Checking for updates...')
39 | try:
40 | repo = Repo(self.repo_dir / repo_name)
41 | current = repo.head.commit
42 | repo.remotes.origin.pull()
43 | if current != repo.head.commit:
44 | print(f'Repository {repo_name} updated.')
45 | else:
46 | print(f'Repository {repo_name} is up-to-date.')
47 | except Exception as e:
48 | print(f'An error occurred: {e}')
49 | raise e
50 | else:
51 | print(f'Cloning repository {repo_name}...')
52 | try:
53 | Repo.clone_from(url, self.repo_dir / repo_name)
54 | print(f'Repository {repo_name} cloned successfully.')
55 | except Exception as e:
56 | print(f'Failed to clone the repository. An error occurred: {e}')
57 | raise e
58 | return str(self.repo_dir / repo_name)
59 |
--------------------------------------------------------------------------------
/symai/extended/seo_query_optimizer.py:
--------------------------------------------------------------------------------
1 | from .. import core
2 | from ..post_processors import StripPostProcessor
3 | from ..pre_processors import PreProcessor
4 | from ..prompts import Prompt
5 | from ..symbol import Expression, Symbol
6 |
7 |
8 | SEO_OPTIMIZER_DESCRIPTION = """[Description]
9 | You are a SEO query optimizer. You are given a list of queries, phrases or sentences and you need to optimize them for search engines.
10 | Assume your search engines are based on vector databases and contain indices of GitHub repositories, papers and other resources.
11 | To retrieve the information cosine similarity is used between semantic embeddings.
12 | To optimize the queries, you need to extract the most information such as relevant entities and relationships between them from the [DATA] and [PAYLOAD] section.
13 | Try to also extract also closely related entities and relationships based on what the user might be looking for, given the provided context.
14 | The number of resulting queries should be between 1 and 8 statements separated by a comma.
15 | """
16 |
17 |
18 | class SEOQueryOptimizerPreProcessor(PreProcessor):
19 | def __call__(self, argument):
20 | return '$> {} =>'.format(str(argument.args[0]))
21 |
22 |
23 | class SEOQueryOptimizer(Expression):
24 | @property
25 | def static_context(self) -> str:
26 | return SEO_OPTIMIZER_DESCRIPTION
27 |
28 | def __init__(self, **kwargs):
29 | super().__init__(**kwargs)
30 | self.sym_return_type = SEOQueryOptimizer
31 |
32 | def forward(self, sym: Symbol, **kwargs) -> Symbol:
33 | @core.few_shot(prompt="Extract relationships between entities:\n",
34 | examples=Prompt([
35 | '$> John has a dog. =>John dog EOF',
36 | '$> How can i find on wikipedia an article about programming? Preferably about python programming. =>Wikipedia python programming tutorial EOF',
37 | '$> Similarly, the term general linguistics is used to distinguish core linguistics from other types of study =>general linguistics term, core linguistics from other types of study EOF',
38 | ]),
39 | pre_processors=[SEOQueryOptimizerPreProcessor()],
40 | post_processors=[StripPostProcessor()],
41 | stop=['EOF'], **kwargs)
42 | def _func(_, text) -> str:
43 | pass
44 |
45 | return _func(self, sym)
46 |
47 |
48 |
--------------------------------------------------------------------------------
/symai/extended/strategies/__init__.py:
--------------------------------------------------------------------------------
1 | from .cot import ChainOfThought
--------------------------------------------------------------------------------
/symai/extended/strategies/cot.py:
--------------------------------------------------------------------------------
1 | from ...models import LLMDataModel
2 | from ...prompts import PromptLanguage, PromptRegistry
3 | from ...strategy import BaseStrategy
4 |
5 | registry = PromptRegistry()
6 | registry.register_instruction(PromptLanguage.ENGLISH, "static_context_chain_of_thoughts",
7 | """
8 | {
9 | "step_by_step": "Q: Your warehouse has 5 pallets of widgets. You purchase 2 more shipments of widgets. Each shipment contains 3 pallets. How many pallets of widgets do you have now?
10 | A: The warehouse started with 5 pallets of widgets. 2 shipments of 3 pallets each is 6 pallets. 5 pallets + 6 pallets = 11 pallets. The answer is 11 pallets.
11 | Q: Your finance department has $23,000 in the budget. If they allocate $20,000 for a marketing campaign and add $6,000 from other savings, how much is left in the budget?"
12 | "answer": "The finance department started with $23,000. They allocated $20,000 for a marketing campaign and added $6,000 from other savings. $23,000 - $20,000 + $6,000 = $9,000. The answer is $9,000."
13 | }
14 | """+
15 | "Think step by step as in the example above before answering."+
16 | "Return the 'step_by_step' and 'answer' properties of the JSON format."
17 | "Always ONLY return a valid JSON format that solve the task.")
18 |
19 |
20 | class Result(LLMDataModel):
21 | step_by_step: str
22 | answer: str
23 |
24 |
25 | class ChainOfThought(BaseStrategy):
26 | def __init__(self, data_model=Result, *args, **kwargs):
27 | super().__init__(data_model=data_model, *args, **kwargs)
28 |
29 | @property
30 | def task(self):
31 | task_str = (
32 | "Think step by step to solve a problem and answer." \
33 | + r'JSON format: {"step_by_step": "string", "answer": "string"}'
34 | )
35 | self.print_verbose(task_str)
36 | return task_str
37 |
38 | @property
39 | def static_context(self):
40 | return registry.instruction("static_context_chain_of_thoughts")
41 |
--------------------------------------------------------------------------------
/symai/extended/summarizer.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from ..components import Clean, Outline, Sequence, Stream, Translate
4 | from ..symbol import Expression, Symbol
5 |
6 |
7 | class Summarizer(Expression):
8 | def __init__(self, filters: List[Expression] = [], **kwargs):
9 | super().__init__(**kwargs)
10 | filters = filters if isinstance(filters, List) or isinstance(filters, tuple) else [filters]
11 | self.data_stream = Stream(Sequence(
12 | Clean(),
13 | Translate(),
14 | Outline(),
15 | *filters,
16 | ))
17 |
18 | def forward(self, sym: Symbol, **kwargs) -> Symbol:
19 | vals = list(self.data_stream(sym, **kwargs))
20 | if len(vals) == 1:
21 | res = {str(vals[0]): Expression(vals[0]).compose(**kwargs)}
22 | return self.sym_return_type(res)
23 | res = Expression(vals).cluster()
24 | sym = res.map()
25 | summary = {}
26 | for k, v in sym.value.items():
27 | summary[k] = Expression(v).compose(**kwargs)
28 | return self.sym_return_type(summary)
29 |
--------------------------------------------------------------------------------
/symai/formatter/__init__.py:
--------------------------------------------------------------------------------
1 | from .regex import CHUNK_REGEX
2 | from .formatter import ParagraphFormatter, SentenceFormatter, RegexFormatter, TextContainerFormatter
--------------------------------------------------------------------------------
/symai/interfaces.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from pydoc import locate
3 |
4 | from .symbol import Expression
5 | from .imports import Import
6 |
7 |
8 | class Interface(Expression):
9 | def __init__(self, *args, **kwargs):
10 | super().__init__(*args, **kwargs)
11 | self.logger = logging.getLogger(__name__)
12 |
13 | def __new__(self, module: str, *args, **kwargs):
14 | module = str(module)
15 | # if `/` in module, assume github repo; else assume local module
16 | if '/' in module:
17 | return Import(module)
18 | module = module.lower()
19 | module = module.replace('-', '_')
20 | self._module = module
21 | self.module_path = f'symai.extended.interfaces.{module}'
22 | return Interface.load_module_class(self.module_path, self._module)(*args, **kwargs)
23 |
24 | def __call__(self, *args, **kwargs):
25 | raise NotImplementedError()
26 |
27 | @staticmethod
28 | def load_module_class(module_path, class_name):
29 | module_ = locate(module_path)
30 | return getattr(module_, class_name)
31 |
--------------------------------------------------------------------------------
/symai/memory.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from . import core_ext
4 | from .symbol import Expression, Symbol
5 | from .components import Function
6 |
7 |
8 | class Memory(Expression):
9 | def __init__(self, **kwargs):
10 | super().__init__(**kwargs)
11 |
12 | def store(self, query: str, *args, **kwargs):
13 | raise NotImplementedError
14 |
15 | def forget(self, query: str, *args, **kwargs):
16 | raise NotImplementedError
17 |
18 | def recall(self, query: str, *args, **kwargs):
19 | raise NotImplementedError
20 |
21 | def forward(self, query: str, *args, **kwargs):
22 | return self.recall(query, *args, **kwargs)
23 |
24 |
25 | class SlidingWindowListMemory(Memory):
26 | def __init__(self, window_size: int = 10, max_size: int = 1000, **kwargs):
27 | super().__init__(**kwargs)
28 | self._memory: List[str] = []
29 | self._window_size: int = window_size
30 | self._max_size: int = max_size
31 |
32 | def store(self, query: str, *args, **kwargs):
33 | self._memory.append(query)
34 | if len(self._memory) > self._max_size:
35 | self._memory = self._memory[-self._max_size:]
36 |
37 | def forget(self, query: Symbol, *args, **kwargs):
38 | self._memory.remove(query)
39 |
40 | def recall(self, *args, **kwargs):
41 | return self._memory[-self._window_size:]
42 |
43 |
44 | class SlidingWindowStringConcatMemory(Memory):
45 | def __init__(self, *args, **kwargs):
46 | super().__init__(**kwargs)
47 | self._memory: str = ''
48 | self.marker: str = '[--++=|=++--]'
49 |
50 | @core_ext.bind(engine='neurosymbolic', property='max_context_tokens')
51 | def max_tokens(self): pass
52 |
53 | def __getstate__(self):
54 | state = super().__getstate__().copy()
55 | # Exclude the max_tokens property from being serialized
56 | state.pop('_max_tokens', None)
57 | return state
58 |
59 | def __setstate__(self, state):
60 | self.__dict__.update(state)
61 | # Initialize _max_tokens as None, it should be set again after deserialization
62 | @core_ext.bind(engine='neurosymbolic', property='max_context_tokens')
63 | def _max_tokens(self): pass
64 | self.max_tokens = _max_tokens
65 |
66 | def history(self):
67 | return [hist for hist in self._memory.split(self.marker) if hist]
68 |
69 | def drop(self):
70 | self._memory = ''
71 |
72 | def store(self, query: str):
73 | # append to string to memory
74 | self._memory += f'{str(query)}{self.marker}'
75 |
76 | def forget(self, query: Symbol, *args, **kwargs):
77 | # remove substring from memory
78 | sym = Symbol(self._memory)
79 | self._memory = str(sym - query)
80 |
81 | def recall(self, query: str, *args, **kwargs) -> Symbol:
82 | hist = self.history()
83 | memory = ''
84 | if len(hist) > 0:
85 | memory = '[MEMORY]\n'
86 | val = '\n'.join(hist)
87 | memory += val
88 | that = self
89 | class Recall(Function):
90 | @property
91 | def static_context(self):
92 | return that.static_context
93 | func = Recall(memory)
94 | return func(query, *args, **kwargs)
95 |
96 |
97 | class VectorDatabaseMemory(Memory):
98 | def __init__(self, enabled: bool = True, top_k: int = 3, index_name: str = 'defaultindex', **kwargs):
99 | super().__init__(**kwargs)
100 | self.enabled: bool = enabled
101 | self.top_k: int = top_k
102 | self.index_name = index_name
103 |
104 | def store(self, query: str , *args, **kwargs):
105 | if not self.enabled: return
106 |
107 | self.add(Symbol(query).zip(), index_name=self.index_name)
108 |
109 | def recall(self, query: str, *args, **kwargs):
110 | if not self.enabled: return
111 |
112 | res = self.get(Symbol(query).embed().value, index_top_k=self.top_k, index_name=self.index_name).ast()
113 |
114 | return [v['metadata']['text'] for v in res['matches']]
115 |
--------------------------------------------------------------------------------
/symai/menu/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/menu/__init__.py
--------------------------------------------------------------------------------
/symai/misc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/misc/__init__.py
--------------------------------------------------------------------------------
/symai/misc/console.py:
--------------------------------------------------------------------------------
1 | import re
2 | import pygments
3 | import logging
4 |
5 | from html import escape as escape_html
6 | from pygments.lexers.python import PythonLexer
7 | from pygments.lexers.javascript import JavascriptLexer
8 | from pygments.lexers.c_cpp import CppLexer
9 | from pygments.lexers.shell import BashLexer
10 | from prompt_toolkit import print_formatted_text
11 | from prompt_toolkit import HTML
12 | from prompt_toolkit.formatted_text import PygmentsTokens
13 |
14 |
15 | logger = logging.getLogger(__name__)
16 | print = print_formatted_text
17 |
18 |
19 | class ConsoleStyle(object):
20 | style_types = {
21 | 'alert': 'orange',
22 | 'error': 'ansired',
23 | 'warn': 'ansiyellow',
24 | 'info': 'ansiblue',
25 | 'success': 'ansigreen',
26 | 'extensity': '#009499',
27 | 'text': 'ansigray',
28 | 'debug': 'gray',
29 | 'custom': 'custom',
30 | 'code': 'code',
31 | 'default': '',
32 | }
33 |
34 | def __init__(self, style_type = '', color = '', logging: bool = False):
35 | self.style_type = style_type
36 | self.color = color
37 | self.logging = logging
38 |
39 | def __call__(self, message):
40 | self.print(message)
41 |
42 | def __enter__(self):
43 | return self
44 |
45 | def __exit__(self, exc_type, exc_val, exc_tb):
46 | pass
47 |
48 | def print(self, message, escape: bool = False):
49 | message = str(message)
50 | if self.logging:
51 | logger.debug(message)
52 | if escape:
53 | message = escape_html(message)
54 | style = self.style_types.get(self.style_type, self.style_types['default'])
55 |
56 | if style == self.style_types['code']:
57 | # Split the message using ``` as delimiter
58 | segments = re.split(r'(```)', message)
59 | is_code = False
60 | for segment in segments:
61 | if segment == '```':
62 | is_code = not is_code
63 | continue
64 | if is_code:
65 | # Determine lexer
66 | if 'python' in segment.lower():
67 | lexer = PythonLexer()
68 | elif 'javascript' in segment.lower() or 'typescript' in segment.lower():
69 | lexer = JavascriptLexer()
70 | elif 'c++' in segment.lower():
71 | lexer = CppLexer()
72 | else:
73 | lexer = BashLexer()
74 | # Print highlighted code
75 | tokens = list(pygments.lex("```" + segment + "```", lexer))
76 | print(PygmentsTokens(tokens), end='')
77 | else:
78 | # Print the segment normally
79 | print(segment, end='\n')
80 | elif style == self.style_types['default']:
81 | print(message)
82 | elif style == self.style_types['custom']:
83 | print(HTML(f''))
84 | else:
85 | print(HTML(f''))
86 |
87 |
--------------------------------------------------------------------------------
/symai/misc/loader.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from itertools import cycle
3 | from shutil import get_terminal_size
4 | from threading import Thread, Event
5 | from time import sleep
6 | from prompt_toolkit import print_formatted_text
7 | from .console import ConsoleStyle
8 |
9 | print = print_formatted_text
10 |
11 | class Loader:
12 | def __init__(self, desc="Loading...", end="\n", timeout=0.1):
13 | self.desc = desc
14 | self.end = end
15 | self.timeout = timeout
16 |
17 | self._thread = Thread(target=self._animate, daemon=True)
18 | self.steps = ["⢿", "⣻", "⣽", "⣾", "⣷", "⣯", "⣟", "⡿"]
19 | self.done = Event()
20 |
21 | def __call__(self, message):
22 | self.print(message)
23 |
24 | def start(self):
25 | self._thread.start()
26 | return self
27 |
28 | def _animate(self):
29 | for c in cycle(self.steps):
30 | if self.done.is_set():
31 | break
32 | with ConsoleStyle('debug'):
33 | sys.stdout.write(f"\r{self.desc} {c} ")
34 | sys.stdout.flush()
35 | sys.stdout.write(f"\r{self.end}")
36 | sys.stdout.flush()
37 | sleep(self.timeout)
38 |
39 | def __enter__(self):
40 | self.start()
41 | return self
42 |
43 | def stop(self):
44 | self.done.set()
45 | self._thread.join()
46 | cols = get_terminal_size((80, 20)).columns
47 | with ConsoleStyle('debug'):
48 | sys.stdout.write("\r" + " " * cols)
49 | sys.stdout.flush()
50 | sys.stdout.write(f"\r{self.end}")
51 | sys.stdout.flush()
52 | print()
53 | print()
54 |
55 | def __exit__(self, exc_type, exc_value, tb):
56 | self.stop()
57 |
58 | def print(self, message):
59 | print(message, style='ansigray')
--------------------------------------------------------------------------------
/symai/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .base import Const, LengthConstraint, LLMDataModel, CustomConstraint
2 | from .errors import ExceptionWithUsage, TypeValidationError
3 |
--------------------------------------------------------------------------------
/symai/models/errors.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 |
3 | class ExceptionWithUsage(Exception):
4 | def __init__(self, message, usage):
5 | super().__init__(message)
6 | self.usage = usage
7 |
8 | class TypeValidationError(Exception):
9 | def __init__(self, prompt: str, result: str, violations: list[str], *args):
10 | super().__init__(*args)
11 | self.prompt = prompt
12 | self.result = result
13 | if violations and not isinstance(violations, list):
14 | violations = [violations]
15 | self.violations = violations
16 |
17 | def __str__(self) -> str:
18 | violations = "\n".join(self.violations)
19 | return (
20 | "[[Prompt]]\n"
21 | f"{self.prompt}"
22 | "\n\n"
23 | "[[Result]]\n"
24 | f"{self.result}"
25 | "\n\n"
26 | "[[Violations]]\n"
27 | f"{violations}"
28 | )
29 |
30 | def __repr__(self):
31 | return f"TypeValidationError({self.prompt}, {self.result}, {self.violations})"
32 |
--------------------------------------------------------------------------------
/symai/ops/__init__.py:
--------------------------------------------------------------------------------
1 | from .primitives import *
2 |
3 | SYMBOL_PRIMITIVES = [
4 | ArithmeticPrimitives,
5 | IterationPrimitives,
6 | ValueHandlingPrimitives,
7 | StringHelperPrimitives,
8 | CastingPrimitives,
9 | ComparisonPrimitives,
10 | ExpressionHandlingPrimitives,
11 | ValidationHandlingPrimitives,
12 | ConstraintHandlingPrimitives,
13 | DataHandlingPrimitives,
14 | UniquenessPrimitives,
15 | PatternMatchingPrimitives,
16 | DictHandlingPrimitives,
17 | QueryHandlingPrimitives,
18 | ExecutionControlPrimitives,
19 | TemplateStylingPrimitives,
20 | DataClusteringPrimitives,
21 | EmbeddingPrimitives,
22 | IndexingPrimitives,
23 | IOHandlingPrimitives,
24 | PersistencePrimitives,
25 | OutputHandlingPrimitives,
26 | FineTuningPrimitives,
27 | ]
--------------------------------------------------------------------------------
/symai/ops/measures.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | from scipy import linalg
4 |
5 |
6 | def calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6):
7 | """Numpy implementation of the Frechet Distance.
8 | The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1)
9 | and X_2 ~ N(mu_2, C_2) is
10 | d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)).
11 |
12 | Stable version by Dougal J. Sutherland.
13 |
14 | Params:
15 | -- mu1 : Numpy array containing the activations of a layer of the
16 | inception net (like returned by the function 'get_predictions')
17 | for generated samples.
18 | -- mu2 : The sample mean over activations, precalculated on an
19 | representative data set.
20 | -- sigma1: The covariance matrix over activations for generated samples.
21 | -- sigma2: The covariance matrix over activations, precalculated on an
22 | representative data set.
23 |
24 | Returns:
25 | -- : The Frechet Distance.
26 | """
27 |
28 | mu1 = np.atleast_1d(mu1).squeeze()
29 | mu2 = np.atleast_1d(mu2).squeeze()
30 |
31 | sigma1 = np.atleast_2d(sigma1)
32 | sigma2 = np.atleast_2d(sigma2)
33 |
34 | assert mu1.shape == mu2.shape, \
35 | 'Training and test mean vectors have different lengths'
36 | assert sigma1.shape == sigma2.shape, \
37 | 'Training and test covariances have different dimensions'
38 |
39 | diff = mu1 - mu2
40 |
41 | # Product might be almost singular
42 | covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)
43 | if not np.isfinite(covmean).all():
44 | msg = ('fid calculation produces singular product; '
45 | 'adding %s to diagonal of cov estimates') % eps
46 | print(msg)
47 | offset = np.eye(sigma1.shape[0]) * eps
48 | covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))
49 |
50 | # Numerical error might give slight imaginary component
51 | if np.iscomplexobj(covmean):
52 | if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):
53 | m = np.max(np.abs(covmean.imag))
54 | raise ValueError('Imaginary component {}'.format(m))
55 | covmean = covmean.real
56 |
57 | tr_covmean = np.trace(covmean)
58 | val = diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean
59 | return val
60 |
61 |
62 | def calculate_mmd(x, y, kernel='rbf', kernel_mul=2.0, kernel_num=5, fix_sigma=None, eps=1e-9):
63 | def gaussian_kernel(source, target, kernel_mul, kernel_num, fix_sigma):
64 | n_samples = source.shape[0] + target.shape[0]
65 | total = np.concatenate([source, target], axis=0)
66 | total0 = np.expand_dims(total, 0)
67 | total1 = np.expand_dims(total, 1)
68 | L2_distance = np.sum((total0 - total1) ** 2, axis=2)
69 |
70 | if fix_sigma:
71 | bandwidth = fix_sigma
72 | else:
73 | bandwidth = np.sum(L2_distance) / (n_samples ** 2 - n_samples + eps)
74 | bandwidth /= kernel_mul ** (kernel_num // 2)
75 | bandwidth_list = [bandwidth * (kernel_mul ** i) for i in range(kernel_num)]
76 | kernel_val = [np.exp(-L2_distance / (bandwidth_temp + eps)) for bandwidth_temp in bandwidth_list]
77 | return np.sum(kernel_val, axis=0)
78 |
79 | def linear_mmd2(f_of_X, f_of_Y):
80 | delta = f_of_X.mean(axis=0) - f_of_Y.mean(axis=0)
81 | loss = np.dot(delta, delta.T)
82 | return loss
83 |
84 | if kernel == 'linear':
85 | return linear_mmd2(x, y)
86 | elif kernel == 'rbf':
87 | batch_size = x.shape[0]
88 | kernels = gaussian_kernel(x, y, kernel_mul=kernel_mul, kernel_num=kernel_num, fix_sigma=fix_sigma)
89 | xx = np.mean(kernels[:batch_size, :batch_size])
90 | yy = np.mean(kernels[batch_size:, batch_size:])
91 | xy = np.mean(kernels[:batch_size, batch_size:])
92 | yx = np.mean(kernels[batch_size:, :batch_size])
93 | loss = xx + yy - xy - yx
94 | return loss
95 |
--------------------------------------------------------------------------------
/symai/processor.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Optional, List, Union
2 | from .pre_processors import PreProcessor
3 | from .post_processors import PostProcessor
4 |
5 |
6 | class ProcessorPipeline:
7 | '''
8 | Base class for all processors.
9 |
10 | Args:
11 | processors: A list of processors to be applied to the response.
12 | '''
13 |
14 | def __init__(self, processors: Optional[List[Union[PreProcessor, PostProcessor]]] = None) -> None:
15 | self.processors = processors
16 |
17 | def __call__(self, *args: Any, **kwds: Any) -> Any:
18 | if self.processors is None or len(self.processors) == 0:
19 | return None
20 |
21 | if isinstance(self.processors[0], PreProcessor):
22 | assert len(args) == 1, f"Expected 1 argument of type Argument, got {len(args)}"
23 | processed_input = ''
24 | for processor in self.processors:
25 | assert isinstance(processor, PreProcessor), f"Expected PreProcessor, got {type(processor)}"
26 | argument = args[0]
27 | t = processor(argument, **kwds)
28 | processed_input += t if t is not None else ''
29 | return processed_input
30 |
31 | elif isinstance(self.processors[0], PostProcessor):
32 | assert len(args) == 2, f"Expected 2 arguments of type Response and Argument, got {len(args)}"
33 | response, argument = args
34 | for processor in self.processors:
35 | assert isinstance(processor, PostProcessor), f"Expected PostProcessor, got {type(processor)}"
36 | response = processor(response, argument, **kwds)
37 | return response
38 |
--------------------------------------------------------------------------------
/symai/server/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/symai/server/llama_cpp_server.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import subprocess
3 | import sys
4 | import os
5 | from loguru import logger
6 |
7 | def llama_cpp_server():
8 | parser = argparse.ArgumentParser(description="A wrapper for llama_cpp.", add_help=False)
9 | parser.add_argument("--help", action="store_true", help="Show available options for llama_cpp server.")
10 | parser.add_argument("--env", choices=['python', 'cpp'], default='python',
11 | help="Choose programming environment (python or cpp)")
12 | parser.add_argument("--cpp-server-path", type=str, help="Path to llama.cpp server executable")
13 |
14 | main_args, llama_cpp_args = parser.parse_known_args()
15 |
16 | if main_args.help:
17 | if main_args.env == 'python':
18 | command = [sys.executable, "-m", "llama_cpp.server", "--help"]
19 | subprocess.run(command)
20 | else:
21 | if not main_args.cpp_server_path:
22 | logger.error("Error: --cpp-server-path is required when using cpp environment")
23 | sys.exit(1)
24 | if not os.path.exists(main_args.cpp_server_path):
25 | logger.error(f"Error: Executable not found at {main_args.cpp_server_path}")
26 | sys.exit(1)
27 | command = [main_args.cpp_server_path, "--help"]
28 | subprocess.run(command)
29 | sys.exit(0)
30 |
31 | if main_args.env == 'cpp':
32 | if not main_args.cpp_server_path:
33 | logger.error("Error: --cpp-server-path is required when using cpp environment")
34 | sys.exit(1)
35 | if not os.path.exists(main_args.cpp_server_path):
36 | logger.error(f"Error: Executable not found at {main_args.cpp_server_path}")
37 | sys.exit(1)
38 | command = [
39 | main_args.cpp_server_path,
40 | *llama_cpp_args,
41 | ]
42 | llama_cpp_args = [arg for arg in llama_cpp_args if not arg.startswith("--embedding")] # Exclude embedding argument
43 | else: # python
44 | command = [
45 | sys.executable,
46 | "-m",
47 | "llama_cpp.server",
48 | *llama_cpp_args,
49 | ]
50 |
51 | return command, llama_cpp_args
52 |
--------------------------------------------------------------------------------
/symai/symsh.md:
--------------------------------------------------------------------------------
1 | "LLM Command Usage and Detail:
2 |
3 | llm (language model) processing is initiated with specific start flags `'`, `"` and `` ` `` and optionally preceded by `.`, `!`, or `?`.
4 |
5 | 1. Normal Mode: `'`, `"` and `` ` ``
6 | Usage: 'Your Query' or "Your Query" or `Your Query`
7 | These commands send 'Your Query' to sym's language model and return the result.
8 |
9 | 2. Stateful Mode: `.'`, `."` and `.`
10 | Usage: .'Your Query' or ."Your Query" or .`Your Query`
11 | These commands create a stateful conversation with sym’s language models. Once you have initiated a stateful conversation
12 | with `.`, continuing with queries using `.` will result in the conversation maintaining a context from all previous queries
13 | and responses.
14 |
15 | 3. Overwrite Mode: `!'`, `!"`, `!`
16 | Usage: !'Your Query' or !"Your Query" or !`Your Query`
17 | These commands function as their normal or stateful counterparts, but also overwrite the existing conversation_state.pickle.
18 |
19 | 4. Search Mode: `?'`, `?"`, `?`
20 | Usage: ?'Your Query' or ?"Your Query" or ?`Your Query`
21 | This command sends 'Your Query' to sym's internal search engine and the results are returned in the shell.
22 |
23 | Note: The use of `'`, `"` or `` ` `` to wrap 'Your Query' does not impact the functionality of the command."
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | You can run the tests with the following command:
2 |
3 | ```bash
4 | pytest
5 | ```
6 |
7 | By default, it runs with the `v --ignore=tests/test_imports` flags.
8 |
9 | We recommend to run the `test_imports.py` file separately, as it should be run only once (or occasionally, when you add new imports to the package):
10 |
11 | ```bash
12 | pytest -q --tb=no tests/test_imports.py
13 | ```
14 |
--------------------------------------------------------------------------------
/tests/engines/drawing/test_drawing_engine.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pathlib import Path
3 |
4 | import pytest
5 |
6 | from symai import Symbol
7 | from symai.backend.settings import SYMAI_CONFIG
8 | from symai.extended import Interface
9 |
10 | DRAWING_ENGINE = SYMAI_CONFIG.get("DRAWING_ENGINE_MODEL")
11 |
12 | OAI_MODELS = ["dall-e-2", "dall-e-3", "gpt-image-1"]
13 | BFL_MODELS = ["flux-dev", "flux-pro", "flux-pro-1.1", "flux-pro-1.1-ultra"]
14 |
15 |
16 | @pytest.mark.skipif(not DRAWING_ENGINE.startswith("dall-e-") and not DRAWING_ENGINE.startswith("gpt-image-"), reason="Not an OpenAI model")
17 | @pytest.mark.parametrize("model", OAI_MODELS)
18 | def test_gpt_image_create(model):
19 | gpt_image = Interface('gpt_image')
20 | paths = gpt_image('a fluffy cat with a cowboy hat', model=model, size=1024, response_format='url')
21 | assert os.path.exists(paths[0]) and os.path.getsize(paths[0]) > 0
22 |
23 |
24 | @pytest.mark.skipif(not DRAWING_ENGINE.startswith("dall-e-") and not DRAWING_ENGINE.startswith("gpt-image-"), reason="Not an OpenAI model")
25 | @pytest.mark.parametrize("model", ['dall-e-2'])
26 | def test_gpt_image_variation(model):
27 | img_path = Path(__file__).parent.parent.parent.parent / 'assets' / 'images' / 'cat.png'
28 | gpt_image = Interface('gpt_image')
29 | paths = gpt_image(
30 | operation='variation',
31 | model=model,
32 | image_path=img_path,
33 | n=1,
34 | size=1024,
35 | response_format='url',
36 | )
37 | assert os.path.exists(paths[0]) and os.path.getsize(paths[0]) > 0
38 |
39 |
40 | @pytest.mark.skipif(not DRAWING_ENGINE.startswith("dall-e-") and not DRAWING_ENGINE.startswith("gpt-image-"), reason="Not an OpenAI model")
41 | @pytest.mark.skipif(DRAWING_ENGINE.startswith("dall-e-"), reason="For whatever reason, this test doesn't work for dall-e-2 even though I followed the documentation. Not sure what's wrong. Maybe it's a bug in the OpenAI API (testing version: openai==1.76.0")
42 | @pytest.mark.parametrize("model,quality", [('gpt-image-1', 'medium')])
43 | def test_gpt_image_edit(model, quality):
44 | img_path = Path(__file__).parent.parent.parent.parent / 'assets' / 'images' / 'cat.png'
45 | gpt_image = Interface('gpt_image')
46 |
47 | prompt = (
48 | "Convert the cat in this image into a character from The Elder Scrolls IV: Oblivion, "
49 | "complete with medieval armor, a nocturnal-fantasy style helmet, zoomed out, and ancient scrolls in the background."
50 | )
51 |
52 | paths = gpt_image(
53 | prompt=prompt,
54 | operation='edit',
55 | model=model,
56 | image_path=img_path,
57 | n=1,
58 | size=1024,
59 | quality=quality,
60 | )
61 |
62 | assert os.path.exists(paths[0]) and os.path.getsize(paths[0]) > 0
63 |
64 |
65 | @pytest.mark.skipif(not DRAWING_ENGINE.startswith("flux"), reason="Not a Black Forest Labs model")
66 | @pytest.mark.parametrize("model", BFL_MODELS)
67 | def test_flux_image_create(model):
68 | flux = Interface('flux')
69 | paths = flux(
70 | "a fluffy cat with a cowboy hat",
71 | operation='create',
72 | model=model,
73 | width=1024,
74 | height=768,
75 | steps=50,
76 | guidance=7.5,
77 | seed=42,
78 | safety_tolerance=2
79 | )
80 | assert os.path.exists(paths[0]) and os.path.getsize(paths[0]) > 0
81 |
82 |
83 | if __name__ == '__main__':
84 | pytest.main()
85 |
--------------------------------------------------------------------------------
/tests/functional/test_apply_preprocessors.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 |
3 | import pytest
4 |
5 | from symai.functional import _apply_preprocessors
6 | from symai.pre_processors import PreProcessor
7 |
8 |
9 | class MockArgument:
10 | def __init__(self, raw_input=False, args=None, instance=""):
11 | self.prop = SimpleNamespace(raw_input=raw_input, instance=instance)
12 | self.args = args or []
13 |
14 | class MockPreProcessor(PreProcessor):
15 | def __call__(self, argument):
16 | return f"Processed: {argument.prop.instance}"
17 |
18 | @pytest.mark.parametrize("raw_input,args,pre_processors,instance,expected", [
19 | (False, [], [MockPreProcessor()], "test_instance", "Processed: test_instance"),
20 | (True, ["arg1", "test_instance"], None, "test_instance", "arg1 test_instance"),
21 | (False, [], None, "test_instance", ""),
22 | (False, ["arg1", "arg2"], None, "test_instance", "arg1 arg2"),
23 | (False, ["test prompt"], [], "test_instance", "test prompt"),
24 | ])
25 | def test_apply_preprocessors(raw_input, args, pre_processors, instance, expected):
26 | argument = MockArgument(raw_input=raw_input, args=args)
27 | result = _apply_preprocessors(argument, instance, pre_processors)
28 | assert result == expected
29 |
30 | def test_apply_preprocessors_multiple():
31 | class AnotherMockPreProcessor(PreProcessor):
32 | def __call__(self, argument):
33 | return f"Also processed: {argument.prop.instance}"
34 |
35 | argument = MockArgument(raw_input=False)
36 | pre_processors = [MockPreProcessor(), AnotherMockPreProcessor()]
37 | result = _apply_preprocessors(argument, "test_instance", pre_processors)
38 | assert result == "Processed: test_instanceAlso processed: test_instance"
39 |
40 | def test_apply_preprocessors_none_return():
41 | class NoneReturnPreProcessor(PreProcessor):
42 | def __call__(self, argument):
43 | return None
44 |
45 | argument = MockArgument(raw_input=False)
46 | pre_processors = [NoneReturnPreProcessor(), MockPreProcessor()]
47 | result = _apply_preprocessors(argument, "test_instance", pre_processors)
48 | assert result == "Processed: test_instance"
49 |
50 | def test_apply_preprocessors_modifying():
51 | class ModifyingPreProcessor(PreProcessor):
52 | def __call__(self, argument):
53 | argument.prop.instance = "modified_" + argument.prop.instance
54 | return argument.prop.instance
55 |
56 | argument = MockArgument(raw_input=False, instance="test_instance")
57 | pre_processors = [ModifyingPreProcessor(), MockPreProcessor()]
58 | result = _apply_preprocessors(argument, "test_instance", pre_processors)
59 | assert result == "modified_test_instanceProcessed: modified_test_instance"
60 |
61 |
62 | def test_apply_preprocessors_exception():
63 | class ExceptionPreProcessor(PreProcessor):
64 | def __call__(self, argument):
65 | raise ValueError("Test exception")
66 |
67 | argument = MockArgument(raw_input=False)
68 | pre_processors = [ExceptionPreProcessor(), MockPreProcessor()]
69 | with pytest.raises(ValueError, match="Test exception"):
70 | _apply_preprocessors(argument, "test_instance", pre_processors)
71 |
--------------------------------------------------------------------------------
/tests/functional/test_limit_num_outputs.py:
--------------------------------------------------------------------------------
1 | from types import SimpleNamespace
2 |
3 | import pytest
4 |
5 | from symai.functional import _limit_number_results
6 |
7 |
8 | @pytest.fixture
9 | def argument():
10 | arg = SimpleNamespace()
11 | arg.prop = SimpleNamespace()
12 | return arg
13 |
14 | class TestLimitNumberResults:
15 | def test_string_list_limit(self, argument):
16 | argument.prop.limit = 2
17 | rsp = ['a', 'b', 'c']
18 | result = _limit_number_results(rsp, argument, str)
19 | assert result == 'a\nb'
20 |
21 | def test_list_limit(self, argument):
22 | argument.prop.limit = 2
23 | rsp = [1, 2, 3, 4]
24 | result = _limit_number_results(rsp, argument, list)
25 | assert result == [1, 2]
26 |
27 | def test_dict_limit(self, argument):
28 | argument.prop.limit = 2
29 | rsp = {'a': 1, 'b': 2, 'c': 3}
30 | result = _limit_number_results(rsp, argument, dict)
31 | assert result == {'a': 1, 'b': 2}
32 |
33 | def test_set_limit(self, argument):
34 | argument.prop.limit = 2
35 | rsp = {1, 2, 3, 4}
36 | result = _limit_number_results(rsp, argument, set)
37 | assert len(result) == 2
38 | assert all(x in {1, 2, 3, 4} for x in result)
39 |
40 | def test_tuple_limit(self, argument):
41 | argument.prop.limit = 2
42 | rsp = (1, 2, 3, 4)
43 | result = _limit_number_results(rsp, argument, tuple)
44 | assert result == (1, 2)
45 |
46 | def test_no_limit(self, argument):
47 | argument.prop.limit = None
48 | rsp = [1, 2, 3, 4]
49 | result = _limit_number_results(rsp, argument, list)
50 | assert result == [1, 2, 3, 4]
51 |
52 | def test_limit_greater_than_length(self, argument):
53 | argument.prop.limit = 10
54 | rsp = [1, 2, 3]
55 | result = _limit_number_results(rsp, argument, list)
56 | assert result == [1, 2, 3]
57 |
58 | def test_non_iterable_input(self, argument):
59 | argument.prop.limit = 2
60 | rsp = 42
61 | result = _limit_number_results(rsp, argument, int)
62 | assert result == 42
63 |
64 | def test_limit_of_one(self, argument):
65 | argument.prop.limit = 1
66 | rsp = [1, 2, 3]
67 | result = _limit_number_results(rsp, argument, list)
68 | assert result == [1]
69 |
70 | def test_empty_input(self, argument):
71 | argument.prop.limit = 2
72 | rsp = []
73 | result = _limit_number_results(rsp, argument, list)
74 | assert result == []
75 |
76 | def test_string_input(self, argument):
77 | argument.prop.limit = 3
78 | rsp = "hello"
79 | result = _limit_number_results(rsp, argument, str)
80 | assert result == "hello"
81 |
82 |
--------------------------------------------------------------------------------
/tests/old/scripts/download_models.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 | from transformers import AutoModelForCausalLM
4 |
5 | # Model candidates:
6 | # - EleutherAI/gpt-j-6B
7 | # - EleutherAI/gpt-neo-125M
8 | # - EleutherAI/gpt-neo-1.3B
9 | # - EleutherAI/gpt-neo-2.7B
10 | # - EleutherAI/gpt-neox-20b
11 | # - togethercomputer/GPT-JT-6B-v1
12 | # - gpt2
13 | # - gpt2-medium
14 | # - gpt2-large
15 | # - gpt2-xl
16 |
17 |
18 | if __name__ == "__main__":
19 | parser = argparse.ArgumentParser()
20 | parser.add_argument("--model_name", type=str, default="EleutherAI/gpt-j-6B")
21 | parser.add_argument("--cache_dir", type=str, default="/system/user/publicwork/dinu/remote/tmp/cache")
22 | args = parser.parse_args()
23 |
24 | # download model
25 | model = AutoModelForCausalLM.from_pretrained(args.model_name, cache_dir=args.cache_dir)
26 | del model
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/old/scripts/single_run.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 | from symai import Expression
4 | from symai.backend.engines.neurosymbolic.engine_nesy_client import NeSyClientEngine
5 | from symai.extended import Solver
6 |
7 |
8 | def setup_engine():
9 | print("Initializing engine...")
10 | engine = NeSyClientEngine()
11 | Expression.register(engines={'neurosymbolic': engine})
12 | return engine
13 |
14 |
15 | def run_single_test():
16 | tests = [
17 | {"test": 'Add the square root of 5 to the square root of x to the power of 2, where x equals 10', "expected": "421.26827"},
18 | {"test": '4 + 433.43 + e^0.38877 - 33.101 / pi', "expected": "428.369"},
19 | {"test": 'John weights 85 pounds. Jeff weighs 105 pounds. Jake weighs 115 pounds. Two of them standing together on the same scale could weigh 200 pounds.', "expected": "Yes. John and Jake could weigh 200 pounds together."},
20 | {"test": "Solve -42*r + 27*c = -1167 and 130*r + 4*c = 372 for r.", "expected": 4},
21 | {"test": 'A line parallel to y = 4x + 6 passes through (5, 10). What is the y-coordinate of the point where this line crosses the y-axis?', "expected": "-10"},
22 | ]
23 |
24 | solver = Solver()
25 | for test in tests:
26 | res = solver(test["test"])
27 | print(res)
28 | #assert res == test["expected"]
29 |
30 |
31 | if __name__ == "__main__":
32 | parser = argparse.ArgumentParser()
33 | parser.add_argument("--save_dir", type=str, default="./evaluation/results")
34 | args = parser.parse_args()
35 |
36 | # setup engine
37 | #engine = setup_engine()
38 |
39 | # run evaluation
40 | print("Running evaluation...")
41 | df_results = run_single_test()
42 | print(df_results)
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/tests/old/test_backend.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from symai import Expression, Symbol
4 | from symai.components import Template
5 |
6 | Expression.command(time_clock=True)
7 |
8 |
9 | class TestBackend(unittest.TestCase):
10 | def test_index(self):
11 | expr = Expression()
12 | s1 = Symbol('Hello World!').zip()
13 | s2 = Symbol('I like cookies!').zip()
14 | expr.add(s1, index_name='defaultindex')
15 | expr.add(s2, index_name='defaultindex')
16 | res = expr.get(Symbol('hello').embed().value).ast()
17 | self.assertTrue('Hello World!' in res, res)
18 |
19 | def test_html_template(self):
20 | template = Template()
21 | res = template(Symbol('Create a table with two columns (title, price).', 'data points: Apple, 1.99; Banana, 2.99; Orange, 3.99'))
22 | self.assertTrue('