├── .gitbook.yaml ├── .github └── FUNDING.yml ├── .gitignore ├── .symai └── symsh.config.json ├── CITATION.cff ├── Dockerfile ├── MANIFEST.in ├── README.md ├── app.py ├── assets ├── images │ ├── banner.png │ ├── cat.jpg │ ├── cat.png │ ├── img1.png │ ├── img10.png │ ├── img2.png │ ├── img3.png │ ├── img4.png │ ├── img5.png │ ├── img6.png │ ├── img7.png │ ├── img8.png │ ├── img9.png │ ├── preview.gif │ ├── screen1.jpeg │ ├── symai_logo.png │ ├── symsh.png │ ├── vid1.png │ ├── vid2.png │ ├── vid3.png │ ├── vid4.png │ ├── vid5.png │ └── vid6.png └── results │ ├── news.html │ ├── news.png │ └── news_prev.png ├── bin ├── install.ps1 └── install.sh ├── build.py ├── docker-compose.yml ├── docs └── source │ ├── ENGINES │ ├── clip_engine.md │ ├── custom_engine.md │ ├── drawing_engine.md │ ├── file_engine.md │ ├── indexing_engine.md │ ├── local_engine.md │ ├── neurosymbolic_engine.md │ ├── ocr_engine.md │ ├── search_engine.md │ ├── speech_engine.md │ ├── symbolic_engine.md │ └── webcrawler_engine.md │ ├── FEATURES │ ├── causal_reasoning.md │ ├── contracts.md │ ├── error_handling.md │ ├── expressions.md │ ├── import.md │ ├── operations.md │ └── vision.md │ ├── INSTALLATION.md │ ├── INTRODUCTION.md │ ├── LICENSE │ ├── QUICKSTART.md │ ├── SUMMARY.md │ ├── TOOLS │ ├── chatbot.md │ ├── packages.md │ └── shell.md │ └── TUTORIALS │ ├── chatbot.md │ ├── context.md │ ├── data_query.md │ ├── examples.md │ └── video_tutorials.md ├── environment.yml ├── icon_converter.py ├── installer.py ├── notebooks ├── Basics.ipynb ├── ChatBot.ipynb ├── Conversation.ipynb ├── Indexer.ipynb ├── News.ipynb ├── Queries.ipynb ├── TTS_Persona.ipynb ├── examples │ ├── Lean engine.png │ ├── a_star.txt │ ├── abstract.py │ ├── audio.mp3 │ ├── dbpedia_samples.jsonl │ ├── dbpedia_samples_prepared_train.jsonl │ ├── dbpedia_samples_prepared_valid.jsonl │ ├── demo.py │ ├── demo_strategy.py │ ├── docs.py │ ├── einsteins_puzzle.txt │ ├── file.json │ ├── lean.py │ ├── news.py │ ├── paper.pdf │ ├── paper.py │ └── sql.py └── outputs │ └── tmp.html.pkl ├── public └── eai.svg ├── pyproject.toml ├── pytest.ini ├── setup.py ├── symai ├── TERMS_OF_SERVICE.md ├── __init__.py ├── backend │ ├── __init__.py │ ├── base.py │ ├── driver │ │ └── webclient.py │ ├── engines │ │ ├── __init__.py │ │ ├── crawler │ │ │ └── engine_selenium.py │ │ ├── drawing │ │ │ ├── engine_bfl.py │ │ │ └── engine_gpt_image.py │ │ ├── embedding │ │ │ ├── engine_llama_cpp.py │ │ │ ├── engine_openai.py │ │ │ └── engine_plugin_embeddings.py │ │ ├── execute │ │ │ └── engine_python.py │ │ ├── files │ │ │ └── engine_io.py │ │ ├── imagecaptioning │ │ │ ├── engine_blip2.py │ │ │ └── engine_llavacpp_client.py │ │ ├── index │ │ │ ├── engine_pinecone.py │ │ │ └── engine_vectordb.py │ │ ├── lean │ │ │ └── engine_lean4.py │ │ ├── neurosymbolic │ │ │ ├── __init__.py │ │ │ ├── engine_anthropic_claudeX_chat.py │ │ │ ├── engine_anthropic_claudeX_reasoning.py │ │ │ ├── engine_deepseekX_reasoning.py │ │ │ ├── engine_google_geminiX_reasoning.py │ │ │ ├── engine_huggingface.py │ │ │ ├── engine_llama_cpp.py │ │ │ ├── engine_openai_gptX_chat.py │ │ │ └── engine_openai_gptX_reasoning.py │ │ ├── ocr │ │ │ └── engine_apilayer.py │ │ ├── output │ │ │ └── engine_stdout.py │ │ ├── search │ │ │ ├── engine_openai.py │ │ │ ├── engine_perplexity.py │ │ │ └── engine_serpapi.py │ │ ├── speech_to_text │ │ │ └── engine_local_whisper.py │ │ ├── symbolic │ │ │ └── engine_wolframalpha.py │ │ ├── text_to_speech │ │ │ └── engine_openai.py │ │ ├── text_vision │ │ │ └── engine_clip.py │ │ └── userinput │ │ │ └── engine_console.py │ ├── mixin │ │ ├── __init__.py │ │ ├── anthropic.py │ │ ├── deepseek.py │ │ ├── google.py │ │ └── openai.py │ └── settings.py ├── chat.py ├── collect │ ├── __init__.py │ ├── dynamic.py │ ├── pipeline.py │ └── stats.py ├── components.py ├── constraints.py ├── core.py ├── core_ext.py ├── endpoints │ ├── __init__py │ └── api.py ├── exceptions.py ├── extended │ ├── __init__.py │ ├── api_builder.py │ ├── arxiv_pdf_parser.py │ ├── bibtex_parser.py │ ├── conversation.py │ ├── crawler.py │ ├── document.py │ ├── file_merger.py │ ├── graph.py │ ├── html_style_template.py │ ├── interfaces │ │ ├── __init__.py │ │ ├── blip_2.py │ │ ├── clip.py │ │ ├── console.py │ │ ├── dall_e.py │ │ ├── file.py │ │ ├── flux.py │ │ ├── gpt_image.py │ │ ├── input.py │ │ ├── llava.py │ │ ├── ocr.py │ │ ├── openai_search.py │ │ ├── perplexity.py │ │ ├── pinecone.py │ │ ├── python.py │ │ ├── selenium.py │ │ ├── serpapi.py │ │ ├── terminal.py │ │ ├── tts.py │ │ ├── vectordb.py │ │ ├── whisper.py │ │ └── wolframalpha.py │ ├── metrics │ │ ├── __init__.py │ │ └── similarity.py │ ├── os_command.py │ ├── packages │ │ ├── __init__.py │ │ ├── symdev.py │ │ ├── sympkg.py │ │ └── symrun.py │ ├── personas │ │ ├── __init__.py │ │ ├── builder.py │ │ ├── dialogue.py │ │ ├── persona.py │ │ ├── research │ │ │ ├── __init__.py │ │ │ └── yann_lecun.py │ │ ├── sales │ │ │ ├── __init__.py │ │ │ └── erik_james.py │ │ └── student │ │ │ ├── __init__.py │ │ │ └── max_tenner.py │ ├── repo_cloner.py │ ├── seo_query_optimizer.py │ ├── solver.py │ ├── strategies │ │ ├── __init__.py │ │ └── cot.py │ ├── summarizer.py │ ├── taypan_interpreter.py │ └── vectordb.py ├── formatter │ ├── __init__.py │ ├── emoji.pytxt │ ├── formatter.py │ └── regex.py ├── functional.py ├── imports.py ├── interfaces.py ├── memory.py ├── menu │ ├── __init__.py │ └── screen.py ├── misc │ ├── __init__.py │ ├── console.py │ └── loader.py ├── models │ ├── __init__.py │ ├── base.py │ └── errors.py ├── ops │ ├── __init__.py │ ├── measures.py │ └── primitives.py ├── post_processors.py ├── pre_processors.py ├── processor.py ├── prompts.py ├── server │ ├── __init__.py │ ├── huggingface_server.py │ └── llama_cpp_server.py ├── shell.py ├── shellsv.py ├── strategy.py ├── symbol.py ├── symsh.md └── utils.py ├── tests ├── README.md ├── contract │ └── test_contract.py ├── data │ └── pg1727.txt ├── engines │ ├── drawing │ │ └── test_drawing_engine.py │ ├── neurosymbolic │ │ └── test_nesy_engine.py │ └── search │ │ ├── openai.py │ │ └── perplexity.py ├── functional │ ├── test_apply_postprocess.py │ ├── test_apply_preprocessors.py │ ├── test_execute_query_fallback.py │ ├── test_limit_num_outputs.py │ └── test_typecasting.py ├── old │ ├── scripts │ │ ├── download_models.py │ │ ├── evaluation.py │ │ └── single_run.py │ ├── test_backend.py │ ├── test_components.py │ ├── test_composition.py │ ├── test_decorators.py │ ├── test_finetuning.py │ ├── test_import.py │ ├── test_interface.py │ └── test_strategy.py ├── test_components.py ├── test_imports.py └── test_misc.py └── trusted_repos.yml /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: symbolicai/docs/source/ 2 | 3 | structure: 4 | readme: INTRODUCTION.md 5 | summary: SUMMARY.md 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [extensityAI] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://www.paypal.com/donate/?hosted_button_id=WCWP5D2QWZXFQ'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | docs/_build/ 13 | docs/.doctrees/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | 133 | results/ 134 | symai.config.json 135 | .DS_Store 136 | ._.DS_Store 137 | **/.DS_Store 138 | **/._.DS_Store 139 | 140 | 141 | # ignore all files in the symai/extended/packages/ directory 142 | notebooks/demos/**/* 143 | .virtual_documents/notebooks 144 | notebooks/.virtual_documents 145 | outputs/ 146 | demos/ 147 | wandb/ 148 | tmp/ 149 | dist/ 150 | bin/ 151 | .idea 152 | scripts 153 | SymbolicAI_Installer_**.* 154 | entitlements.plist 155 | favicon.ico 156 | icon.ico 157 | icon.icns 158 | release_**/* 159 | packages/ExtensityAI/LightRAG-symai 160 | sympkg 161 | symsh 162 | .bash_history 163 | -------------------------------------------------------------------------------- /.symai/symsh.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": { 3 | "completion-menu.completion.current": "bg:#323232 #212121", 4 | "completion-menu.completion": "bg:#800080 #212121", 5 | "scrollbar.background": "bg:#222222", 6 | "scrollbar.button": "bg:#776677", 7 | "history-completion": "bg:#212121 #f5f5f5", 8 | "path-completion": "bg:#800080 #f5f5f5", 9 | "file-completion": "bg:#9040b2 #f5f5f5", 10 | "history-completion-selected": "bg:#efefef #b3d7ff", 11 | "path-completion-selected": "bg:#efefef #b3d7ff", 12 | "file-completion-selected": "bg:#efefef #b3d7ff" 13 | }, 14 | "map-nt-cmd": true, 15 | "show-splash-screen": true 16 | } -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Dinu" 5 | given-names: "Marius-Constantin" 6 | orcid: "https://orcid.org/my-orcid?orcid=0000-0002-7518-3337" 7 | editors: 8 | - family-names: "Leoveanu-Condrei" 9 | given-names: "Claudiu" 10 | title: "SymbolicAI: A Neuro-Symbolic Perspective on Large Language Models (LLMs)" 11 | date-released: 2022-11-30 12 | url: "https://github.com/Xpitfire/symbolicai" 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM pytorch/pytorch:2.0.1-cuda11.7-cudnn8-devel 3 | 4 | LABEL symai.image.authors="extensity.ai" \ 5 | symai.image.vendor="ExtensityAI" \ 6 | symai.image.title="SymbolicAI" \ 7 | symai.image.description="SymbolicAI: Compositional Differentiable Programming Library" \ 8 | symai.image.source="https://github.com/ExtensityAI/symbolicai.git" \ 9 | symai.image.revision="${VCS_REF}" \ 10 | symai.image.created="${BUILD_DATE}" \ 11 | symai.image.documentation="https://symbolicai.readthedocs.io/en/latest/README.html" 12 | LABEL symai.dependencies.versions.torch="2.0.1" 13 | LABEL symai.dependencies.versions.cuda="11.7" 14 | ARG DEBIAN_FRONTEND=noninteractive 15 | 16 | # NVIDIA key migration 17 | RUN apt-key del 7fa2af80 18 | RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/3bf863cc.pub 19 | RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu2004/x86_64/7fa2af80.pub 20 | # Update the base image 21 | RUN apt update && apt upgrade -y 22 | 23 | # Install symai 24 | 25 | ## Install dependencies 26 | RUN apt install -y curl sudo nano git htop netcat wget unzip tmux apt-utils cmake build-essential libgl1-mesa-glx libglib2.0-0 27 | 28 | ## Upgrade pip 29 | RUN pip3 install --upgrade pip 30 | 31 | # Install nvm and pm2 32 | RUN curl -o install_nvm.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh && \ 33 | echo 'fabc489b39a5e9c999c7cab4d281cdbbcbad10ec2f8b9a7f7144ad701b6bfdc7 install_nvm.sh' | sha256sum --check && \ 34 | bash install_nvm.sh 35 | 36 | RUN bash -c "source $HOME/.nvm/nvm.sh && \ 37 | # use node 16 38 | nvm install 16 && \ 39 | # install pm2 40 | npm install --location=global pm2" 41 | 42 | RUN mkdir -p /root/.symai/install 43 | COPY . /root/.symai/install 44 | RUN cd /root/.symai/install && python3 -m pip install ".[all]" 45 | 46 | # Increase ulimit to 1,000,000 47 | RUN prlimit --pid=$PPID --nofile=1000000 48 | 49 | EXPOSE 8999 50 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | global-exclude test_*.py 2 | include symai/**/* 3 | include symai/backend/driver/* 4 | exclude **/*.pyc -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from symai import shell 2 | 3 | shell.run() 4 | -------------------------------------------------------------------------------- /assets/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/banner.png -------------------------------------------------------------------------------- /assets/images/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/cat.jpg -------------------------------------------------------------------------------- /assets/images/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/cat.png -------------------------------------------------------------------------------- /assets/images/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img1.png -------------------------------------------------------------------------------- /assets/images/img10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img10.png -------------------------------------------------------------------------------- /assets/images/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img2.png -------------------------------------------------------------------------------- /assets/images/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img3.png -------------------------------------------------------------------------------- /assets/images/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img4.png -------------------------------------------------------------------------------- /assets/images/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img5.png -------------------------------------------------------------------------------- /assets/images/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img6.png -------------------------------------------------------------------------------- /assets/images/img7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img7.png -------------------------------------------------------------------------------- /assets/images/img8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img8.png -------------------------------------------------------------------------------- /assets/images/img9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/img9.png -------------------------------------------------------------------------------- /assets/images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/preview.gif -------------------------------------------------------------------------------- /assets/images/screen1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/screen1.jpeg -------------------------------------------------------------------------------- /assets/images/symai_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/symai_logo.png -------------------------------------------------------------------------------- /assets/images/symsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/symsh.png -------------------------------------------------------------------------------- /assets/images/vid1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid1.png -------------------------------------------------------------------------------- /assets/images/vid2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid2.png -------------------------------------------------------------------------------- /assets/images/vid3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid3.png -------------------------------------------------------------------------------- /assets/images/vid4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid4.png -------------------------------------------------------------------------------- /assets/images/vid5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid5.png -------------------------------------------------------------------------------- /assets/images/vid6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/images/vid6.png -------------------------------------------------------------------------------- /assets/results/news.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/results/news.png -------------------------------------------------------------------------------- /assets/results/news_prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/assets/results/news_prev.png -------------------------------------------------------------------------------- /bin/install.ps1: -------------------------------------------------------------------------------- 1 | # Check if Git is installed 2 | if (-not (Get-Command git -ErrorAction SilentlyContinue)) { 3 | # Download Git from the official site and install 4 | Invoke-WebRequest -Uri "https://github.com/git-for-windows/git/releases/download/v2.31.0.windows.1/Git-2.31.0-64-bit.exe" -OutFile "$env:TEMP\Git-2.31.0-64-bit.exe" 5 | Start-Process "$env:TEMP\Git-2.31.0-64-bit.exe" -ArgumentList "/VERYSILENT" -Wait 6 | } 7 | 8 | # Check if Anaconda installed 9 | if (-not (Get-Command conda -ErrorAction SilentlyContinue)) { 10 | # Download Anaconda from the official site and install 11 | Invoke-WebRequest -Uri "https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Windows-x86_64.exe" -OutFile "$env:TEMP\Anaconda3-2023.03-1-Windows-x86_64.exe" 12 | Start-Process "$env:TEMP\Anaconda3-2023.03-1-Windows-x86_64.exe" -ArgumentList "/InstallationType=JustMe /AddToPath=0 /RegisterPython=0 /S" -Wait 13 | } 14 | 15 | # Source conda 16 | $env:Path = "$env:USERPROFILE\Anaconda3\Scripts;" + $env:Path 17 | 18 | # Create conda environment named symenv 19 | conda create --name symenv -y 20 | 21 | # Install Jupyter Notebook using conda 22 | conda install -n symenv jupyter -y 23 | 24 | # Activate the conda environment 25 | conda activate symenv 26 | 27 | # Install symbolicai using pip that comes with the Anaconda environment 28 | & "$env:USERPROFILE\Anaconda3\envs\symenv\Scripts\pip" install symbolicai 29 | 30 | # Set up an ipython configuration 31 | if (!(Test-Path -Path "$env:USERPROFILE\.ipython\profile_default\startup")) { 32 | New-Item -ItemType directory -Path "$env:USERPROFILE\.ipython\profile_default\startup" 33 | } 34 | Add-Content "$env:USERPROFILE\.ipython\profile_default\startup\startup.py" "from symai import *; from symai.extended.conversation import Conversation; q = Conversation(auto_print=True)" 35 | 36 | # Create a shortcut for the ipython console on Desktop 37 | $Shortcut = (New-Object -ComObject WScript.Shell).CreateShortcut("$([Environment]::GetFolderPath('Desktop'))\SymbolicAI.lnk") 38 | $Shortcut.TargetPath = "powershell.exe" 39 | $Shortcut.WorkingDirectory = "" # set this to the intended starting directory 40 | $command = '-ExecutionPolicy Bypass -NoExit -Command . "$env:USERPROFILE\Anaconda3\envs\symenv\Scripts\ipython.exe"' 41 | $Shortcut.Arguments = $command 42 | $Shortcut.Save() 43 | -------------------------------------------------------------------------------- /bin/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Check if git is installed 3 | if ! command -v git &> /dev/null 4 | then 5 | # Install git 6 | if [ "$(uname)" == "Darwin" ]; then 7 | # MacOS 8 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 9 | brew install git 10 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 11 | # Linux 12 | sudo apt-get install git -y 13 | fi 14 | fi 15 | 16 | # Check if Anaconda and git are installed 17 | if ! command -v conda &> /dev/null 18 | then 19 | # Check the OS to download the appropriate version of Anaconda 20 | if [ "$(uname)" == "Darwin" ]; then 21 | # MacOS 22 | if [ "$(uname -m)" == "x86_64" ]; then 23 | # Intel Mac 24 | curl -O https://repo.anaconda.com/archive/Anaconda3-2023.03-1-MacOSX-x86_64.sh 25 | bash Anaconda3-2023.03-1-MacOSX-x86_64.sh -b 26 | else 27 | # Apple Silicon Mac 28 | curl -O https://repo.anaconda.com/archive/Anaconda3-2023.03-1-MacOSX-arm64.sh 29 | bash Anaconda3-2023.03-1-MacOSX-arm64.sh -b 30 | fi 31 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 32 | # Linux 33 | wget https://repo.anaconda.com/archive/Anaconda3-2023.03-1-Linux-x86_64.sh 34 | bash Anaconda3-2023.03-1-Linux-x86_64.sh -b 35 | fi 36 | fi 37 | 38 | # Initialize Anaconda3 39 | # Skip initialization if already initialized 40 | if [ -z "$(conda info --envs)" ] 41 | then 42 | eval "$(conda shell.bash hook)" 43 | fi 44 | 45 | # Source conda 46 | source ~/anaconda3/etc/profile.d/conda.sh 47 | 48 | # Create conda environment named symai 49 | conda create --name symenv -y 50 | 51 | # Install Jupyter Notebook using conda 52 | conda install -n symenv jupyter -y 53 | 54 | # Activate the conda environment 55 | conda activate symenv 56 | 57 | # Install symbolicai using pip that comes with the Anaconda environment 58 | ~/anaconda3/envs/symenv/bin/pip install symbolicai 59 | 60 | # Install ipython configuration 61 | mkdir -p ~/.ipython/profile_default/startup 62 | echo "from symai import *; from symai.extended.conversation import Conversation; q = Conversation(auto_print=True)" > ~/.ipython/profile_default/startup/startup.py 63 | 64 | # Create a desktop icon on Linux 65 | if [ "$(uname)" == "Darwin" ]; then 66 | # MacOS 67 | # Generate open-terminal-and-run script 68 | echo "#!/bin/bash 69 | open -a Terminal.app ~/anaconda3/envs/symenv/bin/ipython" > SymbolicAI.app 70 | 71 | # Make the generated script executable 72 | chmod +x SymbolicAI.app 73 | 74 | # Move the script to the Applications directory 75 | mv SymbolicAI.app /Applications/SymbolicAI.app 76 | 77 | # Generate an Automator application that runs the script 78 | echo " 79 | 80 | 81 | 82 | Label 83 | com.user.loginscript 84 | ProgramArguments 85 | 86 | /Applications/SymbolicAI.app 87 | 88 | RunAtLoad 89 | 90 | 91 | " > ~/Library/LaunchAgents/com.user.loginscript.plist 92 | elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then 93 | # Linux 94 | echo "[Desktop Entry] 95 | Version=1.0 96 | Type=Application 97 | Name=Sym AI 98 | Icon=terminal 99 | Exec=bash -c 'source ~/anaconda3/bin/activate symenv && ipython' 100 | Terminal=true" > ~/Desktop/symai.desktop 101 | 102 | # Make the desktop file executable 103 | chmod +x ~/Desktop/symai.desktop 104 | fi -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | symai_service: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | image: extensityai/symbolicai:latest 9 | command: uvicorn symai.endpoints.api:app --reload --host 0.0.0.0 --port 8999 10 | ports: 11 | - 8999:8999 12 | volumes: 13 | - .symai:/root/.symai 14 | -------------------------------------------------------------------------------- /docs/source/ENGINES/clip_engine.md: -------------------------------------------------------------------------------- 1 | # CLIP Engine 2 | 3 | To perform text-based image few-shot classification, we use `CLIP`. This implementation is very experimental, and conceptually does not fully integrate the way we intend it, since the embeddings of CLIP and GPT-3 are not aligned (embeddings of the same word are not identical for both models). Aligning them remains an open problem for future research. For example, one could learn linear projections from one embedding space to the other. 4 | 5 | The following example demonstrates how to classify the image of our generated cat from above and return the results as an array of probabilities: 6 | 7 | ```python 8 | clip = Interface('clip') 9 | res = clip('https://oaidalleapiprodscus.blob.core.windows.net/private/org-l6FsXDfth6...', 10 | ['cat', 'dog', 'bird', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe']) 11 | ``` 12 | 13 | ```bash 14 | :Output: 15 | array([[9.72840726e-01, 6.34790864e-03, 2.59368378e-03, 3.41371237e-03, 16 | 3.71197984e-03, 8.53193272e-03, 1.03346225e-04, 2.08464009e-03, 17 | 1.77942711e-04, 1.94185617e-04]], dtype=float32) 18 | ``` -------------------------------------------------------------------------------- /docs/source/ENGINES/file_engine.md: -------------------------------------------------------------------------------- 1 | # File Engine 2 | 3 | To perform file operations, we use the operating system's file system. Currently, we support only PDF files and plain text files. This is an early stage, and we are working on more sophisticated file system access and remote storage. The following example demonstrates how to read a PDF file and return the text: 4 | 5 | ```python 6 | expr = Expression() 7 | res = expr.open('./LICENSE') 8 | ``` 9 | 10 | ```bash 11 | :Output: 12 | BSD 3-Clause License\n\nCopyright (c) 2023 ... 13 | ``` -------------------------------------------------------------------------------- /docs/source/ENGINES/indexing_engine.md: -------------------------------------------------------------------------------- 1 | # Indexing Engine 2 | 3 | We use `Pinecone` to index and search for text. The following example demonstrates how to store text as an index and then retrieve the most related match: 4 | 5 | ```python 6 | expr = Expression() 7 | expr.add(Symbol('Hello World!').zip()) 8 | expr.add(Symbol('I like cookies!').zip()) 9 | res = expr.get(Symbol('hello').embedding, index_name='default_index').ast() 10 | res['matches'][0]['metadata']['text'][0] 11 | ``` 12 | 13 | ```bash 14 | :Output: 15 | Hello World! 16 | ``` 17 | 18 | Here, the `zip` method creates a pair of strings and embedding vectors, which are then added to the index. The line with `get` retrieves the original source based on the vector value of `hello` and uses `ast` to cast the value to a dictionary. 19 | 20 | You can set several optional arguments for the indexing engine. For more details, see the `symai/backend/engine_pinecone.py` file. -------------------------------------------------------------------------------- /docs/source/ENGINES/ocr_engine.md: -------------------------------------------------------------------------------- 1 | # OCR Engine 2 | 3 | To extract text from images, we can perform optical character recognition (OCR) with `APILayer`. The following example demonstrates how to transcribe an image and return the text: 4 | 5 | ```python 6 | from symai.interfaces import Interface 7 | 8 | ocr = Interface('ocr') 9 | res = ocr('https://media-cdn.tripadvisor.com/media/photo-p/0f/da/22/3a/rechnung.jpg') 10 | ``` 11 | 12 | The OCR engine returns a dictionary with a key `all_text` where the full text is stored. For more details, refer to their documentation [here](https://apilayer.com/marketplace/image_to_text-api). 13 | 14 | ```bash 15 | :Output: 16 | China Restaurant\nMaixim,s\nSegeberger Chaussee 273\n22851 Norderstedt\nTelefon 040/529 16 2 ... 17 | ``` -------------------------------------------------------------------------------- /docs/source/ENGINES/speech_engine.md: -------------------------------------------------------------------------------- 1 | # Speech Engine (Whisper) 2 | 3 | To transcribe audio files, we can perform speech transcription using `whisper`. The following example demonstrates how to transcribe an audio file and return the text: 4 | 5 | ```python 6 | from symai.interfaces import Interface 7 | 8 | speech = Interface('whisper') 9 | res = speech('examples/audio.mp3') 10 | ``` 11 | 12 | ```bash 13 | :Output: 14 | I may have overslept. 15 | ``` -------------------------------------------------------------------------------- /docs/source/ENGINES/symbolic_engine.md: -------------------------------------------------------------------------------- 1 | # Symbolic Engine (WolframAlpha) 2 | 3 | Although our work primarily emphasizes how LLMs can assess symbolic expressions, many formal statements have already been efficiently implemented in existing symbolic engines, such as WolframAlpha. Therefore, with an API KEY from WolframAlpha, we can use their engine by using the `Interface('wolframalpha')`. This avoids error-prone evaluations from neuro-symbolic engines for mathematical operations. The following example demonstrates how to use WolframAlpha to compute the result of the variable `x`: 4 | 5 | ```python 6 | from symai import Interface 7 | expression = Interface('wolframalpha') 8 | res = expression('x^2 + 2x + 1') 9 | ``` 10 | 11 | ```bash 12 | :Output: 13 | x = -1 14 | ``` -------------------------------------------------------------------------------- /docs/source/ENGINES/webcrawler_engine.md: -------------------------------------------------------------------------------- 1 | # Webcrawler Engine 2 | 3 | To access data from the web, we can use `Selenium`. The following example demonstrates how to crawl a website and return the results: 4 | 5 | ```python 6 | from symai.interfaces import Interface 7 | 8 | crawler = Interface('selenium') 9 | res = crawler(url="https://www.google.com/", 10 | pattern="google") 11 | ``` 12 | The `pattern` property can be used to verify if the document has been loaded correctly. If the pattern is not found, the crawler will timeout and return an empty result. 13 | 14 | ```bash 15 | :Output: 16 | GoogleKlicke hier, wenn du nach einigen Sekunden nicht automatisch weitergeleitet wirst.GmailBilderAnmelden ... 17 | ``` -------------------------------------------------------------------------------- /docs/source/FEATURES/vision.md: -------------------------------------------------------------------------------- 1 | # Vision 2 | 3 | SymbolicAI allows image inputs to models that support vision. A URL or local image can be passed directly into a Symbol like so: 4 | 5 | ```python 6 | # Supported formats: jpg, jpeg, png, webp 7 | image = "https://example.com/image.png" 8 | image_description = Symbol(f"Analyze this image <> and describe it.").interpret() 9 | 10 | 11 | image = "path/to/local/image.jpg" 12 | image_description = Symbol(f"Analyze this image <> and describe it.").interpret() 13 | 14 | 15 | images = [ 16 | "https://example.com/image1.png", 17 | "https://example.com/image2.png", 18 | "https://example.com/image3.png" 19 | ] 20 | rank = Symbol(f"Given the following images, please rank them in order of appeal." 21 | + f"Images: \n<>\n<>\n<>\n<>\n<>").interpret() 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/source/LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | BSD 3-Clause License 5 | 6 | Copyright (c) 2024, ExtensityAI FlexCo. 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | * Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /docs/source/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](INTRODUCTION.md) 4 | * [Installation](INSTALLATION.md) 5 | * [Quick Start Guide](QUICKSTART.md) 6 | 7 | ## Engines 8 | * [Neuro-Symbolic Engine](ENGINES/neurosymbolic_engine.md) 9 | * [CLIP Engine](ENGINES/clip_engine.md) 10 | * [Custom Engine](ENGINES/custom_engine.md) 11 | * [Drawing Engine](ENGINES/drawing_engine.md) 12 | * [File Engine](ENGINES/file_engine.md) 13 | * [Indexing Engine](ENGINES/indexing_engine.md) 14 | * [Local Engine](ENGINES/local_engine.md) 15 | * [OCR Engine](ENGINES/ocr_engine.md) 16 | * [Search Engine](ENGINES/search_engine.md) 17 | * [Speech Engine](ENGINES/speech_engine.md) 18 | * [Symbolic Engine](ENGINES/symbolic_engine.md) 19 | * [Webcrawler Engine](ENGINES/webcrawler_engine.md) 20 | 21 | ## Features 22 | * [Operations](FEATURES/operations.md) 23 | * [Expressions](FEATURES/expressions.md) 24 | * [Contracts](FEATURES/contracts.md) 25 | * [Causal Reasoning](FEATURES/causal_reasoning.md) 26 | * [Error Handling](FEATURES/error_handling.md) 27 | * [Vision](FEATURES/vision.md) 28 | * [Module Import](FEATURES/import.md) 29 | 30 | ## Tutorials 31 | * [Example Notebooks](TUTORIALS/examples.md) 32 | * [Chatbot Tutorial](TUTORIALS/chatbot.md) 33 | * [Handling Large Contexts](TUTORIALS/context.md) 34 | * [SQL Data Query](TUTORIALS/data_query.md) 35 | * [Video Tutorials](TUTORIALS/video_tutorials.md) 36 | 37 | ## Tools 38 | * [Chatbot CLI](TOOLS/chatbot.md) 39 | * [Package Manager](TOOLS/packages.md) 40 | * [Shell](TOOLS/shell.md) 41 | 42 | ## License 43 | * [License](LICENSE) 44 | -------------------------------------------------------------------------------- /docs/source/TOOLS/chatbot.md: -------------------------------------------------------------------------------- 1 | # Chatbot 2 | 3 | You can engage in a basic conversation with `Symbia`, a chatbot that uses `SymbolicAI` to detect the content of your request and switch between different contextual modes to answer your questions. These modes include search engines, speech engines, and more. To start the chatbot, simply run: 4 | 5 | ```bash 6 | $> symchat 7 | ``` 8 | 9 | This will launch a chatbot interface: 10 | 11 | ```bash 12 | Symbia: Hi there! I'm Symbia, your virtual assistant. How may I help you? 13 | $> 14 | ``` 15 | 16 | To exit the conversation, type `exit`, `quit`, or press `Ctrl+C`. 17 | You can also load our chatbot `SymbiaChat` into a jupyter notebook and process step-wise requests. -------------------------------------------------------------------------------- /docs/source/TUTORIALS/chatbot.md: -------------------------------------------------------------------------------- 1 | # Writing a Custom Chatbot 2 | 3 | Click here for [interactive notebook](https://github.com/ExtensityAI/symbolicai/blob/main/notebooks/ChatBot.ipynb) 4 | 5 | ```python 6 | import os 7 | import warnings 8 | warnings.filterwarnings('ignore') 9 | os.chdir('../') # set the working directory to the root of the project 10 | from symai import * 11 | from symai.components import * 12 | from IPython.display import display 13 | ``` 14 | 15 | Writing a chatbot is fairly easy with our framework. All we need to do is basically derive from the ChatBot class and implement the forward method. The base class ChatBot has already some helper capabilities and context detection dictionaries. All we have to do is use the self.narrate method to instruct our chatbot to say what we want. 16 | 17 | Afterwards, we can use the self.context_choice method to classify the context of the user input. This is done by using a dictionary of context keywords. The self.context_choice method returns the context key that matches the user input. This key can then be used to determine the next action / condition of the chatbot. 18 | 19 | By creating an instance of the SymbiaChat and calling the forward method, we can start a chat with our chatbot. The forward method takes a user input and returns a chatbot response. 20 | 21 | See the following example: 22 | 23 | ```python 24 | from symai.chat import ChatBot 25 | from symai.interfaces import Interface 26 | 27 | 28 | class SymbiaChat(ChatBot): 29 | def forward(self) -> str: 30 | message = self.narrate('Symbia introduces herself, writes a greeting message and asks how to help.') 31 | while True: 32 | # query user 33 | usr = self.input(message) 34 | 35 | # detect context 36 | ctxt = self.context_choice(usr) 37 | 38 | if 'option 3' in ctxt: # exit 39 | self.narrate('Symbia writes goodbye message.', end=True) 40 | break # end chat 41 | 42 | elif 'option 4' in ctxt: # help 43 | message = self.narrate('Symbia writes for each capability one sentence.', 44 | context=self.capabilities) 45 | 46 | elif 'option 1' in ctxt: # chit chat 47 | message = self.narrate('Symbia replies to the user question in a casual way.') 48 | 49 | elif 'option 2' in ctxt: 50 | # detect command 51 | option = self.capabilities_choice(usr) 52 | 53 | if 'option 1' in option: 54 | q = usr.extract('user query request') 55 | rsp = self.search(q) 56 | message = self.narrate('Symbia replies to the user based on the online search results.', 57 | context=rsp) 58 | elif 'option 2' in option: 59 | q = usr.extract('URL') 60 | site = self.crawler(q) 61 | site.save('tmp.html') 62 | message = self.narrate('Symbia explains that the website is downloaded to the `tmp.html` file.') 63 | elif 'option 3' in option: 64 | pass 65 | 66 | # TODO ... 67 | ``` 68 | 69 | ```python 70 | from symai.chat import SymbiaChat 71 | 72 | chat = SymbiaChat() 73 | chat() 74 | ``` 75 | 76 | The implemented chatbot can answer trivia questions, use the Google search engine to retrieve information, and download and save web pages. -------------------------------------------------------------------------------- /docs/source/TUTORIALS/examples.md: -------------------------------------------------------------------------------- 1 | # Example Files 2 | 3 | * [Indexer](https://github.com/ExtensityAI/symbolicai/blob/main/notebooks/Indexer.ipynb) 4 | * [Conversation](https://github.com/ExtensityAI/symbolicai/blob/main/notebooks/Conversation.ipynb) 5 | * [Text to Speech](https://github.com/ExtensityAI/symbolicai/blob/main/notebooks/TTS_Persona.ipynb) -------------------------------------------------------------------------------- /docs/source/TUTORIALS/video_tutorials.md: -------------------------------------------------------------------------------- 1 | # Video Tutorials 2 | 3 | | Date | Title | Video | 4 | | ---- | ---- | ---- | 5 | | 2nd Dec. 2023 | Use ChatGPT and off-the-shelf RAG on Terminal/Command Prompt/Shell | [![Use ChatGPT and off-the-shelf RAG on Terminal/Command Prompt/Shell](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid6.png)](https://youtu.be/RJ6i_b91nQE?si=jZR4LJRZQZVAm4MA) | 6 | | 21st Nov. 2023 | Virtual Persona from Documents, Multi-Agent Chat, Text-to-Speech to hear your Personas | [![Virtual Persona from Documents, Multi-Agent Chat, Text-to-Speech to hear your Personas](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid5.png)](https://www.youtube.com/watch?v=-o2315T9348) | 7 | | 1st Aug. 2023 | Automatic Retrieval Augmented Generation, Multimodal Inputs, User Packages | [![Automatic Retrieval Augmented Generation, Multimodal Inputs, User Packages](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid4.png)](https://www.youtube.com/watch?v=0AqB6SEvRqo) | 8 | | 22nd July 2023 | ChatBot In-Depth Demonstration (Tool Use and Iterative Processing) | [![ChatBot In-Depth Demonstration (Tool Use and Iterative Processing)](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid3.png)](https://www.youtube.com/watch?v=R46SskmmrCE) | 9 | | 1st July 2023 | Symbols, Operations, Expressions, LLM-based functions! | [![Symbols, Operations, Expressions, LLM-based functions!](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid2.png)](https://www.youtube.com/watch?v=Ch9ygW62A34) | 10 | | 9th June 2023 | The future is neuro-symbolic: Expressiveness of ChatGPT and generalizability of symbols | [![The future is neuro-symbolic: Expressiveness of ChatGPT and generalizability of symbols](https://raw.githubusercontent.com/ExtensityAI/symbolicai/main/assets/images/vid1.png)](https://www.youtube.com/watch?v=RW_7JdXvbRA) | 11 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: symai 2 | 3 | channels: 4 | - default 5 | - conda-forge 6 | - pytorch 7 | - nvidia 8 | 9 | dependencies: 10 | - python 11 | - setuptools 12 | - toml 13 | - natsort 14 | - numpy 15 | - tqdm 16 | - python-box 17 | - scikit-learn 18 | - pytorch 19 | - pytorch-cuda 20 | - torchvision 21 | - torchaudio 22 | - pyyaml 23 | - transformers 24 | - openai 25 | - pypdf 26 | - ipython 27 | - beautifulsoup4 28 | - selenium 29 | - rpyc 30 | - accelerate 31 | - sentencepiece 32 | - webdriver-manager 33 | - pandas 34 | - tika 35 | - pip 36 | - paramiko # For SSH protocol operations 37 | - docker-py # Docker SDK for Python 38 | 39 | - pip: 40 | - sympy 41 | - symbolicai[all] 42 | -------------------------------------------------------------------------------- /notebooks/examples/Lean engine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/notebooks/examples/Lean engine.png -------------------------------------------------------------------------------- /notebooks/examples/a_star.txt: -------------------------------------------------------------------------------- 1 | function reconstruct_path(cameFrom, current) 2 | total_path := {current} 3 | while current in cameFrom.Keys: 4 | current := cameFrom[current] 5 | total_path.prepend(current) 6 | return total_path 7 | 8 | // A* finds a path from start to goal. 9 | // h is the heuristic function. h(n) estimates the cost to reach goal from node n. 10 | function A_Star(start, goal, h) 11 | // The set of discovered nodes that may need to be (re-)expanded. 12 | // Initially, only the start node is known. 13 | // This is usually implemented as a min-heap or priority queue rather than a hash-set. 14 | openSet := {start} 15 | 16 | // For node n, cameFrom[n] is the node immediately preceding it on the cheapest path from start 17 | // to n currently known. 18 | cameFrom := an empty map 19 | 20 | // For node n, gScore[n] is the cost of the cheapest path from start to n currently known. 21 | gScore := map with default value of Infinity 22 | gScore[start] := 0 23 | 24 | // For node n, fScore[n] := gScore[n] + h(n). fScore[n] represents our current best guess as to 25 | // how cheap a path could be from start to finish if it goes through n. 26 | fScore := map with default value of Infinity 27 | fScore[start] := h(start) 28 | 29 | while openSet is not empty 30 | // This operation can occur in O(Log(N)) time if openSet is a min-heap or a priority queue 31 | current := the node in openSet having the lowest fScore[] value 32 | if current = goal 33 | return reconstruct_path(cameFrom, current) 34 | 35 | openSet.Remove(current) 36 | for each neighbor of current 37 | // d(current,neighbor) is the weight of the edge from current to neighbor 38 | // tentative_gScore is the distance from start to the neighbor through current 39 | tentative_gScore := gScore[current] + d(current, neighbor) 40 | if tentative_gScore < gScore[neighbor] 41 | // This path to neighbor is better than any previous one. Record it! 42 | cameFrom[neighbor] := current 43 | gScore[neighbor] := tentative_gScore 44 | fScore[neighbor] := tentative_gScore + h(neighbor) 45 | if neighbor not in openSet 46 | openSet.add(neighbor) 47 | 48 | // Open set is empty but goal was never reached 49 | return failure 50 | -------------------------------------------------------------------------------- /notebooks/examples/abstract.py: -------------------------------------------------------------------------------- 1 | from symai import * 2 | from symai.components import * 3 | 4 | HEADER_DESCRIPTION = """Design a web app with HTML, CSS and inline JavaScript. 5 | Use dark theme and best practices for colors, text font, etc. 6 | Use Bootstrap for styling. 7 | Do NOT remove the {{placeholder}} tag and do NOT add new tags into the body!""" 8 | 9 | 10 | class Abstract(Expression): 11 | 12 | def static_context(self): 13 | return HEADER_DESCRIPTION 14 | 15 | def __init__(self, path: str, filters: List[Expression] = [], **kwargs): 16 | super().__init__(**kwargs) 17 | self.path = path 18 | filters = filters if isinstance(filters, List) or isinstance(filters, tuple) else [filters] 19 | self.data_stream = Stream(Sequence( 20 | Clean(), 21 | Translate(), 22 | *filters, 23 | Compose(f'Write a paper summary. Keep all information with the corresponding citations:\n'), 24 | )) 25 | 26 | def forward(self, **kwargs) -> Symbol: 27 | res = self.open(self.path, **kwargs) 28 | data = '' 29 | template = self.header_style(self.html_template) 30 | paragraphs = [] 31 | for section in self.data_stream(res): 32 | key = section.unique(paragraphs) 33 | paragraphs.append(str(key)) 34 | self.html_template_seq.template_ = str(template) 35 | kwargs['dynamic_context'] = f'Context: Create a summary paragraph about {str(key)}\n' 36 | data += '\n'.join([str(s) for s in self.html_stream(section, **kwargs)]) 37 | res = Symbol(str(template).replace('{{placeholder}}', data)) 38 | return res 39 | -------------------------------------------------------------------------------- /notebooks/examples/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/notebooks/examples/audio.mp3 -------------------------------------------------------------------------------- /notebooks/examples/demo.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import symai.core as ai 4 | 5 | 6 | class Demo(ai.Expression): 7 | def __init__(self, value = '', **kwargs) -> None: 8 | super().__init__(value, **kwargs) 9 | 10 | @ai.zero_shot(prompt="Generate a random integer between 0 and 10.", 11 | constraints=[ 12 | lambda x: x >= 0, 13 | lambda x: x <= 10 14 | ]) 15 | def get_random_int(self) -> int: 16 | pass 17 | 18 | @ai.few_shot(prompt="Generate Japanese names: ", 19 | examples=ai.Prompt(["愛子", "和花", "一郎", "和枝"]), 20 | limit=2, 21 | constraints=[lambda x: len(x) > 1]) 22 | def generate_japanese_names(self) -> list: 23 | pass 24 | 25 | @ai.equals() 26 | def equals_to(self, other) -> bool: 27 | pass 28 | 29 | @ai.compare(operator='>') 30 | def larger_than(self, other) -> bool: 31 | pass 32 | 33 | @ai.case(enum=['angry', 'happy', 'annoyed', 'confused', 'satisfied', 'unknown'], 34 | default='unknown') 35 | def sentiment_analysis(self, text: str) -> str: 36 | pass 37 | 38 | @ai.translate() 39 | def translate(self, text: str, language: str) -> str: 40 | pass 41 | 42 | @ai.zero_shot(default=False, 43 | pre_processors=[ai.ArgsPreProcessor("Are this {} names?"), 44 | ai.ArgsToInputPreProcessor(skip=[0])], 45 | post_processors=[ai.ConfirmToBoolPostProcessor()]) 46 | def is_name(self, language: str, text: str) -> bool: 47 | pass 48 | 49 | @ai.extract() 50 | def extract_pattern(self, pattern: str) -> str: 51 | pass 52 | 53 | @ai.clean() 54 | def clean_text(self, text: str) -> str: 55 | pass 56 | 57 | @ai.summarize() 58 | def summarize_text(self, text: str) -> str: 59 | pass 60 | 61 | @ai.expression() 62 | def evaluate_expression(self, expr: str) -> int: 63 | pass 64 | 65 | @ai.simulate() 66 | def simulate_code(self, code: str) -> str: 67 | pass 68 | 69 | @ai.code() 70 | def generate_code(self, descr: str) -> str: 71 | pass 72 | 73 | @ai.outline() 74 | def create_outline(self, text: str) -> str: 75 | pass 76 | 77 | @ai.compose() 78 | def formulize_text(self, outline: str) -> str: 79 | pass 80 | 81 | @ai.replace() 82 | def replace_substring(self, replace: str, value: str) -> str: 83 | pass 84 | 85 | @ai.rank() 86 | def rank_list(self, measure: str, list_: List, order: str) -> str: 87 | pass 88 | 89 | @ai.notify(subscriber={ 90 | 'email': lambda x: print('Email sent to ...', x), 91 | 'slack': lambda x: print('Slack message sent to ...', x), 92 | 'fruits': lambda x: Exception('Fruits are not supported yet.') 93 | }) 94 | def notify_subscriber(self, *args) -> str: 95 | pass 96 | 97 | def __str__(self) -> str: 98 | return str(self.value) 99 | 100 | def __repr__(self) -> str: 101 | return str(self.value) 102 | -------------------------------------------------------------------------------- /notebooks/examples/demo_strategy.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pydoc import locate 3 | 4 | from symai import Expression 5 | 6 | 7 | class Strategy(Expression): 8 | """A base class for implementing strategies.""" 9 | 10 | def __init__(self, *args, **kwargs): 11 | """Initialize the strategy object. 12 | 13 | Args: 14 | *args: Variable length argument list. 15 | **kwargs: Arbitrary keyword arguments. 16 | """ 17 | super().__init__(*args, **kwargs) 18 | self.logger = logging.getLogger(__name__) 19 | 20 | def __new__(self, module: str, *args, **kwargs): 21 | """Create a new instance of the strategy. 22 | 23 | Args: 24 | module (str): The module name of the strategy. 25 | *args: Variable length argument list. 26 | **kwargs: Arbitrary keyword arguments. 27 | 28 | Returns: 29 | Strategy: An instance of the strategy. 30 | 31 | Raises: 32 | NotImplementedError: If __call__ method is not implemented. 33 | """ 34 | module = module.lower() 35 | module = module.replace('-', '_') 36 | self._module = module 37 | self.module_path = f'symai.extended.strategies.{module}' 38 | return Strategy.load_module_class(self.module_path, self._module)(*args, **kwargs) 39 | 40 | def __call__(self, *args, **kwargs): 41 | """Call the strategy. 42 | 43 | Args: 44 | *args: Variable length argument list. 45 | **kwargs: Arbitrary keyword arguments. 46 | 47 | Raises: 48 | NotImplementedError: If not overridden by child class. 49 | """ 50 | raise NotImplementedError() 51 | 52 | @staticmethod 53 | def load_module_class(module_path, class_name): 54 | """Load a module class dynamically. 55 | 56 | Args: 57 | module_path (str): The path of the module. 58 | class_name (str): The name of the class. 59 | 60 | Returns: 61 | class: The loaded module class. 62 | 63 | Raises: 64 | AttributeError: If the module or class is not found. 65 | """ 66 | module_ = locate(module_path) 67 | return getattr(module_, class_name) -------------------------------------------------------------------------------- /notebooks/examples/einsteins_puzzle.txt: -------------------------------------------------------------------------------- 1 | Let us assume that there are five houses of different colors next to each other on the same road. In each house lives a man of a different nationality. Every man has his favorite drink, his favorite brand of cigarettes, and keeps pets of a particular kind. 2 | 3 | The Englishman lives in the red house. 4 | The Swede keeps dogs. 5 | The Dane drinks tea. 6 | The green house is just to the left of the white one. 7 | The owner of the green house drinks coffee. 8 | The Pall Mall smoker keeps birds. 9 | The owner of the yellow house smokes Dunhills. 10 | The man in the center house drinks milk. 11 | The Norwegian lives in the first house. 12 | The Blend smoker has a neighbor who keeps cats. 13 | The man who smokes Blue Masters drinks bier. 14 | The man who keeps horses lives next to the Dunhill smoker. 15 | The German smokes Prince. 16 | The Norwegian lives next to the blue house. 17 | The Blend smoker has a neighbor who drinks water. 18 | 19 | The question to be answered is: Who keeps fish? -------------------------------------------------------------------------------- /notebooks/examples/file.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Test Decorators", 6 | "type": "python", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/tests/test_decorators.py", 9 | "console": "integratedTerminal", 10 | "justMyCode": true, 11 | "env": { 12 | "PYTHONPATH": "${workspaceFolder}", 13 | "OPENAI_API_KEY": "", 14 | "CUDA_VISIBLE_DEVICES": "1" 15 | } 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /notebooks/examples/lean.py: -------------------------------------------------------------------------------- 1 | from symai.backend.engines.lean.engine_lean4 import LeanEngine 2 | # Example usage 3 | if __name__ == "__main__": 4 | # Initialize LeanEngine 5 | engine = LeanEngine() 6 | 7 | # Sample Lean code 8 | code = ''' 9 | theorem and_commutative (A B : Prop) : A ∧ B → B ∧ A := 10 | fun h : A ∧ B => ⟨h.right, h.left⟩dsdsdds 11 | ''' 12 | 13 | # Execute the Lean code 14 | print("Running LeanEngine with a sample Lean theorem...") 15 | results, metadata = engine.forward(code) 16 | 17 | # Check if results are valid and print the output 18 | if results: 19 | print("Results:", results[0].value['output']) 20 | print("Metadata:", metadata) 21 | -------------------------------------------------------------------------------- /notebooks/examples/paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/notebooks/examples/paper.pdf -------------------------------------------------------------------------------- /notebooks/examples/paper.py: -------------------------------------------------------------------------------- 1 | from symai import * 2 | from symai.components import * 3 | 4 | 5 | HEADER_STYLE_DESCRIPTION = """Design a web app with HTML, CSS and inline JavaScript. 6 | Use dark theme and best practices for colors, text font, etc. 7 | Use Bootstrap for styling. 8 | Do NOT remove the {{placeholder}} tag and do NOT add new tags into the body!""" 9 | 10 | HTML_TEMPLATE = """ 11 | 12 | 13 | 14 | 15 | Paper Summary 16 | 17 | 18 | 19 | {{placeholder}} 20 | 21 | """ 22 | 23 | HTML_STREAM_STYLE_DESCRIPTION = """Style the elements according to the bootstrap library. 24 | Replace the list items with a summary title and the item text. 25 | Add highlighting animations. 26 | Use best practices for colors, text font, etc.""" 27 | 28 | 29 | class Paper(Expression): 30 | def __init__(self, path: str, filters: List[Expression] = [], **kwargs): 31 | super().__init__(**kwargs) 32 | self.path = path 33 | filters = filters if isinstance(filters, List) or isinstance(filters, tuple) else [filters] 34 | self.data_stream = Stream(Sequence( 35 | Clean(), 36 | Translate(), 37 | *filters, 38 | Compose(f'Write a paper summary. Keep all information with the corresponding citations:\n'), 39 | )) 40 | self.header_style = Style(description=HEADER_STYLE_DESCRIPTION, 41 | libraries=['https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css', 42 | 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js', 43 | 'https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js']) 44 | 45 | self.html_template = Symbol(HTML_TEMPLATE) 46 | self.html_template_seq = Template() 47 | self.html_stream = Stream( 48 | Sequence( 49 | self.html_template_seq, 50 | Style(description=HTML_STREAM_STYLE_DESCRIPTION) 51 | ) 52 | ) 53 | 54 | def forward(self, **kwargs) -> Symbol: 55 | res = self.open(self.path, **kwargs) 56 | data = '' 57 | template = self.header_style(self.html_template) 58 | paragraphs = [] 59 | for section in self.data_stream(res): 60 | key = section.unique(paragraphs) 61 | paragraphs.append(str(key)) 62 | self.html_template_seq.template_ = str(template) 63 | kwargs['dynamic_context'] = f'Context: Create a summary paragraph about {str(key)}\n' 64 | data += '\n'.join([str(s) for s in self.html_stream(section, **kwargs)]) 65 | res = Symbol(str(template).replace('{{placeholder}}', data)) 66 | return res 67 | -------------------------------------------------------------------------------- /notebooks/examples/sql.py: -------------------------------------------------------------------------------- 1 | import symai as ai 2 | from symai.post_processors import StripPostProcessor, CodeExtractPostProcessor 3 | from symai.pre_processors import PreProcessor 4 | from symai.symbol import Expression, Symbol 5 | 6 | 7 | SQL_CONTEXT = """[Description] 8 | The following statements describe the Structured Query Language (SQL): 9 | 10 | Commonly used SQL commands with examples: 11 | Most SQL commands are used with operators to modify or reduce the scope of data operated on by the statement. Some commonly used SQL commands, along with examples of SQL statements using those commands, follow. 12 | 13 | SQL SELECT. The SELECT command is used to get some or all data in a table. SELECT can be used with operators to narrow down the amount of data selected. 14 | SQL CREATE. The CREATE command is used to create a new SQL database or SQL table. Most versions of SQL create a new database by creating a new directory, in which tables and other database objects are stored as files. 15 | SQL DELETE. The DELETE command removes rows from a named table. 16 | The CREATE TABLE command is used create a table in SQL. 17 | 18 | [Examples] 19 | // Select the title, author and publication date columns from a table named catalog. 20 | SELECT title, author, pub_date 21 | FROM catalog 22 | WHERE pub_date = 2021; 23 | 24 | // The following CREATE DATABASE statement creates a new SQL database named Human_Resources: 25 | SQL: CREATE DATABASE Human_Resources; 26 | 27 | // The following statement creates a table named Employees that has three columns: employee_ID, last_name and first_name, with the first column storing integer (int) data and the other columns storing variable character data of type varchar and a maximum of 255 characters. 28 | SQL: CREATE TABLE Employees ( 29 | employee_ID int, 30 | last_name varchar(255), 31 | first_name varchar(255) 32 | ); 33 | 34 | // All records of employees with the last name Smithee are deleted: 35 | SQL: DELETE FROM Employees WHERE last_name='Smithee'; 36 | 37 | [IMPORTANT] 38 | ALWAYS RETURN ONLY THE ENTIRE SQL STATEMENTS, NOT PARTIAL RESULT LIKE {} ... OR OTHER PLACEHOLDER! 39 | NOT VALID: 40 | {} limit 1 41 | VALID: 42 | SELECT * FROM table limit 1 43 | 44 | [Last Example] 45 | -------------- 46 | """ 47 | 48 | 49 | class SQLPreProcessor(PreProcessor): 50 | def __call__(self, argument): 51 | return '// {} SQL:'.format(str(argument.prop.instance)) 52 | 53 | 54 | class SQL(Expression): 55 | @property 56 | def static_context(self): 57 | return SQL_CONTEXT 58 | 59 | def forward(self, sym: Symbol, *args, **kwargs): 60 | @ai.few_shot(prompt="Generate queries based on the SQL domain specific language description\n", 61 | examples=[], 62 | pre_processors=[SQLPreProcessor()], 63 | post_processors=[StripPostProcessor(), CodeExtractPostProcessor()], 64 | stop=[';'], **kwargs) 65 | def _func(_) -> str: 66 | pass 67 | return SQL(_func(SQL(sym))) 68 | 69 | -------------------------------------------------------------------------------- /notebooks/outputs/tmp.html.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/notebooks/outputs/tmp.html.pkl -------------------------------------------------------------------------------- /public/eai.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "setuptools-scm"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "symbolicai" 7 | dynamic = ["version"] 8 | authors = [ 9 | {name = "Marius-Constantin Dinu", email = "office@extensity.ai"}, 10 | ] 11 | description = "A Neuro-Symbolic Framework for Large Language Models" 12 | readme = "README.md" 13 | requires-python = ">=3.10" 14 | keywords = ["probabilistic programming", "machine learning"] 15 | license = {file = "LICENSE"} 16 | classifiers = [ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: BSD License", 19 | "Operating System :: OS Independent", 20 | ] 21 | dependencies = [ 22 | "attrs>=23.2.0", 23 | "setuptools>=70.0.0", 24 | "toml>=0.10.2", 25 | "loguru>=0.7.3", 26 | "aiohttp>=3.11.13", 27 | "natsort>=8.3.1", 28 | "numpy>=1.26.4,<=2.1.3", 29 | "tqdm>=4.66.3", 30 | "python-box>=7.1.1", 31 | "pytest>=8.3.1", 32 | "pandas>=2.2.2", 33 | "scikit-learn>=1.5.0", 34 | "torch>=2.2.2", 35 | "torchaudio>=2.2.2", 36 | "torchvision>=0.17.2", 37 | "PyYAML>=6.0.1", 38 | "transformers>=4.45.2", 39 | "sympy>=1.12", 40 | "openai>=1.60.0", 41 | "anthropic>=0.43.1", 42 | "google-genai>=1.16.1", 43 | "pypdf>=4.3.0", 44 | "ipython>=8.24.0", 45 | "accelerate>=0.33.0", 46 | "sentencepiece>=0.2.0", 47 | "sentence-transformers>=2.5.1", 48 | "tiktoken>=0.8.0", 49 | "tika>=2.6.0", 50 | "beautifulsoup4>=4.12.3", 51 | "colorama>=0.4.6", 52 | "GitPython>=3.1.42", 53 | "pathos>=0.3.2", 54 | "prompt-toolkit>=3.0.43", 55 | "pydub>=0.25.1", 56 | "opencv-python>=4.8.1.78", 57 | "pymongo>=3.12.3, <4.8", 58 | "requests-toolbelt>=1.0.0", 59 | "pyvis>=0.3.2", 60 | "beartype>=0.18.2", 61 | "pydantic>=2.8.2", 62 | "pydantic-core>=2.20.1", 63 | "pydantic-settings>=2.3.4", 64 | "pycryptodome>=3.20.0", 65 | "httpx>=0.27.2", 66 | "nest-asyncio>=1.6.0", 67 | "rich>=13.9.4" 68 | ] 69 | 70 | [project.optional-dependencies] 71 | bitsandbytes = ["bitsandbytes>=0.43.1"] # handle separately because of Apple Silicon 72 | blip2 = ["decord>=0.6.0", "salesforce-lavis>=1.0.0", "opencv-python-headless>=4.5.5.64"] 73 | hf = ["transformers>=4.45.2", "accelerate>=0.33.0", "peft>=0.13.1", "datasets>=3.0.1", "trl>=0.11.3"] 74 | llama_cpp = ["llama-cpp-python[server]>=0.3.7"] # handle separately since this dependency may not compile and require special maintenance 75 | wolframalpha = ["wolframalpha>=5.0.0"] 76 | whisper = ["openai-whisper>=20240930", "numba>=0.60.0"] 77 | selenium = ["selenium>=4.18.1", "webdriver-manager>=4.0.2", "chromedriver-autoinstaller>=0.6.4"] 78 | serpapi = ["google_search_results>=2.4.2"] 79 | pinecone = ["pinecone-client>=4.1.0"] 80 | bard = ["bardapi>=1.0.0"] 81 | services = ["fastapi>=0.110.0", "redis>=5.0.2", "uvicorn>=0.27.1"] 82 | solver = ["z3-solver>=4.12.6.0"] 83 | all = [ 84 | "symbolicai[hf]", 85 | "symbolicai[wolframalpha]", 86 | "symbolicai[whisper]", 87 | "symbolicai[selenium]", 88 | "symbolicai[serpapi]", 89 | "symbolicai[pinecone]", 90 | "symbolicai[bard]", 91 | "symbolicai[services]", 92 | "symbolicai[solver]" 93 | ] 94 | 95 | [tool.setuptools.dynamic] 96 | version = {attr = "symai.SYMAI_VERSION"} 97 | 98 | [tool.setuptools.package-data] 99 | "*" = ["*.json", "*.md", "*.pytxt"] 100 | 101 | [tool.setuptools.packages.find] 102 | include = ["symai*"] 103 | exclude = ["tests", "examples", "notebooks", "outputs", "assets", "app.py"] 104 | 105 | [project.urls] 106 | "Homepage" = "https://extensity.ai" 107 | "GitHub" = "https://github.com/ExtensityAI/symbolicai" 108 | 109 | [project.scripts] 110 | symchat = "symai.chat:run" 111 | symsh = "symai.shell:run" 112 | sympkg = "symai.extended.packages.sympkg:run" 113 | symdev = "symai.extended.packages.symdev:run" 114 | symrun = "symai.extended.packages.symrun:run" 115 | symconfig = "symai:display_config" 116 | symserver = "symai:run_server" 117 | symwzd = "symai:run_setup_wizard" 118 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = tests 3 | addopts = -v --ignore=tests/test_imports.py --deselect=tests/engines/neurosymbolic/test_nesy_engine.py::test_token_truncator 4 | markers = 5 | mandatory: tests that must pass 6 | 7 | # Ignore 8 | norecursedirs = tests/old 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup() -------------------------------------------------------------------------------- /symai/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/backend/__init__.py -------------------------------------------------------------------------------- /symai/backend/engines/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/backend/engines/__init__.py -------------------------------------------------------------------------------- /symai/backend/engines/crawler/engine_selenium.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | from bs4 import BeautifulSoup 3 | 4 | from ...base import Engine 5 | from ...driver.webclient import connect_browsers, dump_page_source, page_loaded 6 | from ....symbol import Result 7 | 8 | 9 | class SeleniumResult(Result): 10 | def __init__(self, value, **kwargs) -> None: 11 | super().__init__(value, **kwargs) 12 | if value is not None: 13 | self.raw = value 14 | self._value = self.extract() 15 | 16 | def extract(self): 17 | tmp = self.value if isinstance(self.value, list) else [self.value] 18 | res = [] 19 | for r in tmp: 20 | if r is None: 21 | continue 22 | soup = BeautifulSoup(r, 'html.parser') 23 | text = soup.getText() 24 | res.append(text) 25 | res = None if len(res) == 0 else '\n'.join(res) 26 | return res 27 | 28 | 29 | class SeleniumEngine(Engine): 30 | def __init__(self, debug: bool = False): 31 | super().__init__() 32 | self.debug = debug 33 | self.driver_handler = None 34 | self.name = self.__class__.__name__ 35 | 36 | def _init_crawler_engine(self): 37 | self.driver_handler = connect_browsers(debug=False, proxy=None) 38 | 39 | def get_page_source(self, url: str, pattern: str, script: Callable = None) -> str: 40 | # deprecated 41 | driver = self.driver_handler() 42 | try: 43 | driver.get(url) 44 | with page_loaded(driver, pattern, debug=self.debug): 45 | if script: driver.execute_script(script) 46 | return driver.page_source 47 | except Exception as ex: 48 | if self.debug: dump_page_source(driver) 49 | if self.debug: print(ex) 50 | return f"Sorry, I cannot find the page you are looking for: {ex}" 51 | 52 | def id(self) -> str: 53 | return 'crawler' 54 | 55 | def forward(self, argument): 56 | urls, patterns = argument.prop.prepared_input 57 | urls = urls if isinstance(urls, list) else [urls] 58 | 59 | patterns = patterns if isinstance(patterns, list) else [patterns] 60 | assert len(urls) == len(patterns) 61 | rsp = [] 62 | 63 | self._init_crawler_engine() 64 | 65 | for url, p in zip(urls, patterns): 66 | page = self.get_page_source(url=url, pattern=p) 67 | rsp.append(page) 68 | 69 | metadata = {} 70 | rsp = SeleniumResult(rsp) 71 | return [rsp], metadata 72 | 73 | def prepare(self, argument): 74 | assert not argument.prop.processed_input, "CrawlerEngine does not support processed_input." 75 | assert argument.prop.urls, "CrawlerEngine requires urls." 76 | 77 | argument.prop.urls = [str(argument.prop.url)] 78 | argument.prop.patterns = [str(argument.prop.pattern)] 79 | 80 | # be tolerant to kwarg or arg and assign values of urls and patterns 81 | # assign urls 82 | if len(argument.args) >= 1: 83 | argument.prop.urls = argument.args[0] 84 | elif len(argument.kwargs) >= 1: 85 | keys = list(argument.kwargs.keys()) 86 | argument.prop.urls = argument.kwargs[keys[0]] 87 | # assign patterns 88 | if len(argument.args) >= 2: 89 | argument.prop.patterns = argument.args[1] 90 | elif len(argument.kwargs) >= 2: 91 | keys = list(argument.kwargs.keys()) 92 | argument.prop.patterns = argument.kwargs[keys[1]] 93 | 94 | argument.prop.prepared_input = (argument.prop.urls, argument.prop.patterns) 95 | -------------------------------------------------------------------------------- /symai/backend/engines/embedding/engine_openai.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Optional 3 | 4 | import numpy as np 5 | import openai 6 | 7 | from ...base import Engine 8 | from ...mixin.openai import OpenAIMixin 9 | from ...settings import SYMAI_CONFIG 10 | 11 | logging.getLogger("openai").setLevel(logging.ERROR) 12 | logging.getLogger("requests").setLevel(logging.ERROR) 13 | logging.getLogger("urllib").setLevel(logging.ERROR) 14 | logging.getLogger("httpx").setLevel(logging.ERROR) 15 | logging.getLogger("httpcore").setLevel(logging.ERROR) 16 | 17 | 18 | class EmbeddingEngine(Engine, OpenAIMixin): 19 | def __init__(self, api_key: Optional[str] = None, model: Optional[str] = None): 20 | super().__init__() 21 | logger = logging.getLogger('openai') 22 | logger.setLevel(logging.WARNING) 23 | self.config = SYMAI_CONFIG 24 | if self.id() != 'embedding': 25 | return # do not initialize if not embedding; avoids conflict with llama.cpp check in EngineRepository.register_from_package 26 | openai.api_key = self.config['EMBEDDING_ENGINE_API_KEY'] if api_key is None else api_key 27 | self.model = self.config['EMBEDDING_ENGINE_MODEL'] if model is None else model 28 | self.max_tokens = self.api_max_context_tokens() 29 | self.embedding_dim = self.api_embedding_dims() 30 | self.name = self.__class__.__name__ 31 | 32 | def id(self) -> str: 33 | if self.config.get('EMBEDDING_ENGINE_API_KEY') and self.config['EMBEDDING_ENGINE_MODEL'].startswith('text-embedding'): 34 | return 'embedding' 35 | return super().id() # default to unregistered 36 | 37 | def command(self, *args, **kwargs): 38 | super().command(*args, **kwargs) 39 | if 'EMBEDDING_ENGINE_API_KEY' in kwargs: 40 | openai.api_key = kwargs['EMBEDDING_ENGINE_API_KEY'] 41 | if 'EMBEDDING_ENGINE_MODEL' in kwargs: 42 | self.model = kwargs['EMBEDDING_ENGINE_MODEL'] 43 | 44 | def forward(self, argument): 45 | prepared_input = argument.prop.prepared_input 46 | args = argument.args 47 | kwargs = argument.kwargs 48 | 49 | inp = prepared_input if isinstance(prepared_input, list) else [prepared_input] 50 | except_remedy = kwargs.get('except_remedy') 51 | new_dim = kwargs.get('new_dim') 52 | 53 | try: 54 | res = openai.embeddings.create(model=self.model, input=inp) 55 | except Exception as e: 56 | if except_remedy is None: 57 | raise e 58 | callback = openai.embeddings.create 59 | res = except_remedy(e, inp, callback, self, *args, **kwargs) 60 | 61 | if new_dim: 62 | mn = min(new_dim, self.embedding_dim) #@NOTE: new_dim should be less than or equal to the original embedding dim 63 | output = [self._normalize_l2(r.embedding[:mn]) for r in res.data] 64 | else: 65 | output = [r.embedding for r in res.data] 66 | 67 | metadata = {"raw_output": res} 68 | 69 | return [output], metadata 70 | 71 | def prepare(self, argument): 72 | assert not argument.prop.processed_input, "EmbeddingEngine does not support processed_input." 73 | argument.prop.prepared_input = argument.prop.entries 74 | 75 | def _normalize_l2(self, x): 76 | x = np.array(x) 77 | if x.ndim == 1: 78 | norm = np.linalg.norm(x) 79 | if norm == 0: 80 | return x.tolist() 81 | return (x / norm).tolist() 82 | else: 83 | norm = np.linalg.norm(x, 2, axis=1, keepdims=True) 84 | return np.where(norm == 0, x, x / norm).tolist() 85 | -------------------------------------------------------------------------------- /symai/backend/engines/embedding/engine_plugin_embeddings.py: -------------------------------------------------------------------------------- 1 | from ...base import Engine 2 | from ...settings import SYMAI_CONFIG 3 | 4 | 5 | class PluginEmbeddingEngine(Engine): 6 | def id(self) -> str: 7 | if not SYMAI_CONFIG['EMBEDDING_ENGINE_API_KEY'] and not SYMAI_CONFIG['EMBEDDING_ENGINE_MODEL'].startswith("llama"): 8 | from ....functional import EngineRepository 9 | 10 | # Register the embedding engine from the plugin 11 | EngineRepository.register_from_plugin('embedding', plugin='ExtensityAI/embeddings', kwargs={'model': 'all-mpnet-base-v2'}, allow_engine_override=True) 12 | return super().id() # do not register this engine as we want the plugin to be used 13 | -------------------------------------------------------------------------------- /symai/backend/engines/execute/engine_python.py: -------------------------------------------------------------------------------- 1 | from ...base import Engine 2 | from ....symbol import Result 3 | 4 | 5 | def full_stack(): 6 | import sys 7 | import traceback 8 | exc = sys.exc_info()[0] 9 | stack = traceback.extract_stack()[-10:-1] # last one would be full_stack() 10 | if exc is not None: # i.e. an exception is present 11 | del stack[-1] # remove call of full_stack, the printed exception 12 | # will contain the caught exception caller instead 13 | trc = 'Traceback (most recent call last):\n' 14 | stackstr = trc + ''.join(traceback.format_list(stack)) 15 | if exc is not None: 16 | stackstr += ' ' + traceback.format_exc().lstrip(trc) 17 | return stackstr 18 | 19 | 20 | class PythonEngine(Engine): 21 | """PythonEngine executes Python code dynamically while managing the code execution 22 | context using provided global and local namespaces. This engine encapsulates the 23 | execution process, handling both successful execution and exception scenarios. 24 | 25 | Usage: 26 | - Instantiate the PythonEngine. 27 | - Call the forward method with the desired Python code and context variables. 28 | 29 | The forward method expects a 'prompt' containing the executable code and can also 30 | accept 'globals', 'locals', 'input_handler', 'output_handler', and 'metadata' as keyword 31 | arguments to manage the execution context and handle input/output operations. 32 | 33 | The Python code should contain a 'run' function which serves as the entry point. The 34 | run function can accept any arguments (*args, **kwargs) and return any type of value. 35 | The code execution result is encapsulated in 'res', which is then managed by the 36 | local or global namespace. 37 | 38 | Parameters: 39 | *args: Variable length argument list. 40 | **kwargs: Arbitrary keyword arguments containing 'prompt' and context variables. 41 | 42 | Returns: 43 | A list containing the results of the code execution within a context dict and a 44 | metadata dict capturing execution details. 45 | 46 | Example: 47 | 48 | engine = PythonEngine() 49 | code = ''' 50 | def run(*args, **kwargs): 51 | return "Hello, " + args[0] 52 | res = run(value) 53 | ''' 54 | results, metadata = engine.forward(prompt=code, globals={}, locals={}, value='World') 55 | 56 | Note: 57 | The 'run' function must be defined, and its execution result must be assigned to 'res'. 58 | In case of an exception, the engine will provide a full traceback to help with debugging. 59 | """ 60 | 61 | def __init__(self): 62 | super().__init__() 63 | self.name = self.__class__.__name__ 64 | 65 | def id(self) -> str: 66 | return 'execute' 67 | 68 | def forward(self, argument): 69 | code = argument.prop.prepared_input 70 | kwargs = argument.kwargs 71 | 72 | globals_ = kwargs['globals'] if 'globals' in kwargs else {} 73 | locals_ = kwargs['locals'] if 'locals' in kwargs else {} 74 | input_handler = kwargs['input_handler'] if 'input_handler' in kwargs else None 75 | if input_handler: 76 | input_handler((code,)) 77 | 78 | rsp = None 79 | err = None 80 | try: 81 | exec(str(code), globals_, locals_) 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 | rsp = Result(rsp) 88 | except Exception as e: 89 | err = e 90 | raise e 91 | 92 | metadata = {} 93 | metadata['error'] = None if not err else full_stack() 94 | 95 | return [rsp], metadata 96 | 97 | def prepare(self, argument): 98 | assert not argument.prop.processed_input, "PythonEngine does not support processed_input." 99 | argument.prop.prepared_input = str(argument.prop.instance) 100 | -------------------------------------------------------------------------------- /symai/backend/engines/imagecaptioning/engine_blip2.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import requests 4 | import torch 5 | 6 | try: 7 | from lavis.models import load_model_and_preprocess 8 | except ImportError: 9 | load_model_and_preprocess = None 10 | 11 | from PIL import Image 12 | 13 | from ...base import Engine 14 | from ...settings import SYMAI_CONFIG 15 | 16 | 17 | class Blip2Engine(Engine): 18 | def __init__(self): 19 | super().__init__() 20 | self.config = SYMAI_CONFIG 21 | ids = self.config['CAPTION_ENGINE_MODEL'].split('/') 22 | if len(ids) != 2: 23 | # return unregistered engine 24 | return 25 | self.name_id = ids[0] 26 | self.model_id = ids[1] 27 | self.model = None # lazy loading 28 | self.vis_processors = None # lazy loading 29 | self.txt_processors = None # lazy loading 30 | self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 31 | self.name = self.__class__.__name__ 32 | 33 | def id(self) -> str: 34 | if self.config['CAPTION_ENGINE_MODEL'] and \ 35 | 'blip2' in self.config['CAPTION_ENGINE_MODEL']: 36 | return 'imagecaptioning' 37 | return super().id() # default to unregistered 38 | 39 | def command(self, *args, **kwargs): 40 | super().command(*args, **kwargs) 41 | if 'CAPTION_ENGINE_MODEL' in kwargs: 42 | self.model_id = kwargs['CAPTION_ENGINE_MODEL'] 43 | 44 | def forward(self, argument): 45 | if load_model_and_preprocess is None: 46 | raise ImportError('Blip2 is not installed. Please install it with `pip install symbolicai[blip2]`') 47 | if self.model is None: 48 | self.model, self.vis_processors, self.txt_processors = load_model_and_preprocess(name = self.name_id, 49 | model_type = self.model_id, 50 | is_eval = True, 51 | device = self.device) 52 | 53 | image, prompt = argument.prop.prepared_input 54 | kwargs = argument.kwargs 55 | except_remedy = kwargs['except_remedy'] if 'except_remedy' in kwargs else None 56 | 57 | if 'http' in image: 58 | image = Image.open(requests.get(image, stream=True).raw).convert('RGB') 59 | elif '/' in image or '\\' in image: 60 | image = Image.open(image).convert('RGB') 61 | 62 | try: 63 | image = self.vis_processors['eval'](image).unsqueeze(0).to(self.device) 64 | prompt = self.txt_processors['eval'](prompt) 65 | res = self.model.generate(samples={"image": image, "prompt": prompt}, use_nucleus_sampling=True, num_captions=3) 66 | except Exception as e: 67 | if except_remedy is None: 68 | raise e 69 | callback = self.model.generate 70 | res = except_remedy(self, e, callback, argument) 71 | 72 | metadata = {} 73 | 74 | return [res], metadata 75 | 76 | def prepare(self, argument): 77 | assert not argument.prop.processed_input, "Blip2Engine does not support processed_input." 78 | argument.prop.prepared_input = (argument.prop.image, argument.prop.prompt) 79 | -------------------------------------------------------------------------------- /symai/backend/engines/neurosymbolic/__init__.py: -------------------------------------------------------------------------------- 1 | from ...mixin import (ANTHROPIC_CHAT_MODELS, ANTHROPIC_REASONING_MODELS, 2 | DEEPSEEK_CHAT_MODELS, DEEPSEEK_REASONING_MODELS, 3 | GOOGLE_CHAT_MODELS, GOOGLE_REASONING_MODELS, 4 | OPENAI_CHAT_MODELS, OPENAI_REASONING_MODELS) 5 | from .engine_anthropic_claudeX_chat import ClaudeXChatEngine 6 | from .engine_anthropic_claudeX_reasoning import ClaudeXReasoningEngine 7 | from .engine_deepseekX_reasoning import DeepSeekXReasoningEngine 8 | from .engine_google_geminiX_reasoning import GeminiXReasoningEngine 9 | from .engine_openai_gptX_chat import GPTXChatEngine 10 | from .engine_openai_gptX_reasoning import GPTXReasoningEngine 11 | 12 | # create the mapping 13 | ENGINE_MAPPING = { 14 | **{model_name: ClaudeXChatEngine for model_name in ANTHROPIC_CHAT_MODELS}, 15 | **{model_name: ClaudeXReasoningEngine for model_name in ANTHROPIC_REASONING_MODELS}, 16 | **{model_name: DeepSeekXReasoningEngine for model_name in DEEPSEEK_REASONING_MODELS}, 17 | **{model_name: GeminiXReasoningEngine for model_name in GOOGLE_REASONING_MODELS}, 18 | **{model_name: GPTXChatEngine for model_name in OPENAI_CHAT_MODELS}, 19 | **{model_name: GPTXReasoningEngine for model_name in OPENAI_REASONING_MODELS}, 20 | } 21 | -------------------------------------------------------------------------------- /symai/backend/engines/ocr/engine_apilayer.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from pathlib import Path 3 | 4 | from typing import Optional 5 | 6 | from ...base import Engine 7 | from ...settings import SYMAI_CONFIG 8 | from ....symbol import Result 9 | 10 | 11 | class ApiLayerResult(Result): 12 | def __init__(self, text, status_code=200, **kwargs): 13 | super().__init__(**kwargs) 14 | self.raw = text 15 | try: 16 | dict_ = self._to_symbol(text).ast() 17 | self._value = dict_['all_text'] if 'all_text' in dict_ else f'OCR Engine Error: {text} - status code {status_code}' 18 | except: 19 | self._value = f'OCR Engine Error: {text} - status code {status_code}' 20 | 21 | 22 | class OCREngine(Engine): 23 | def __init__(self, api_key: Optional[str] = None): 24 | super().__init__() 25 | # Opening JSON file 26 | self.config = SYMAI_CONFIG 27 | self.headers = { 28 | "apikey": self.config['OCR_ENGINE_API_KEY'] if api_key is None else api_key 29 | } 30 | self.name = self.__class__.__name__ 31 | 32 | def id(self) -> str: 33 | if self.config['OCR_ENGINE_API_KEY']: 34 | return 'ocr' 35 | return super().id() # default to unregistered 36 | 37 | def command(self, *args, **kwargs): 38 | super().command(*args, **kwargs) 39 | if 'OCR_ENGINE_API_KEY' in kwargs: 40 | self.headers = { 41 | "apikey": kwargs['OCR_ENGINE_API_KEY'] 42 | } 43 | 44 | def forward(self, argument): 45 | kwargs = argument.kwargs 46 | image_url = argument.prop.image 47 | 48 | if image_url.startswith("file://"): 49 | file_path = Path(image_url[7:]).resolve() 50 | with open(file_path, "rb") as file: 51 | payload = file.read() 52 | url = "https://api.apilayer.com/image_to_text/upload" 53 | response = requests.request("POST", url, headers=self.headers, data=payload) 54 | else: 55 | payload = {} 56 | url = f"https://api.apilayer.com/image_to_text/url?url={image_url}" 57 | response = requests.request("GET", url, headers=self.headers, data = payload) 58 | 59 | status_code = response.status_code 60 | rsp = response.text 61 | rsp = ApiLayerResult(response.text, status_code) 62 | metadata = {} 63 | 64 | return [rsp], metadata 65 | 66 | def prepare(self, argument): 67 | assert not argument.prop.processed_input, "OCREngine does not support processed_input." 68 | image = str(argument.prop.image) 69 | argument.prop.prepared_input = image 70 | -------------------------------------------------------------------------------- /symai/backend/engines/output/engine_stdout.py: -------------------------------------------------------------------------------- 1 | from ...base import Engine 2 | 3 | 4 | class OutputEngine(Engine): 5 | def __init__(self): 6 | super().__init__() 7 | 8 | def id(self) -> str: 9 | return 'output' 10 | 11 | def forward(self, argument): 12 | expr, processed, args, kwargs = argument.prop.prepared_input 13 | res = None 14 | args = [] if args is None else args 15 | kwargs = {} if kwargs is None else kwargs 16 | if expr: 17 | if processed: 18 | res = expr(processed, *args, **kwargs) 19 | else: 20 | res = expr(*args, **kwargs) 21 | 22 | metadata = {} 23 | result = { 24 | 'result': res, 25 | 'processed': processed, 26 | 'args': args, 27 | 'kwargs': kwargs 28 | } 29 | 30 | return [result], metadata 31 | 32 | def prepare(self, argument): 33 | argument.prop.prepared_input = argument.prop.expr, argument.prop.processed_input, argument.prop.args, argument.prop.kwargs 34 | -------------------------------------------------------------------------------- /symai/backend/engines/search/engine_serpapi.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from IPython.utils import io 4 | 5 | from ....symbol import Result 6 | from ....utils import CustomUserWarning 7 | from ...base import Engine 8 | from ...settings import SYMAI_CONFIG 9 | 10 | try: 11 | from serpapi import GoogleSearch 12 | except: 13 | GoogleSearch = None 14 | 15 | 16 | class SearchResult(Result): 17 | def __init__(self, value, **kwargs) -> None: 18 | super().__init__(value, **kwargs) 19 | if 'answer_box' in value.keys() and 'answer' in value['answer_box'].keys(): 20 | self._value = value['answer_box']['answer'] 21 | elif 'answer_box' in value.keys() and 'snippet' in value['answer_box'].keys(): 22 | self._value = value['answer_box']['snippet'] 23 | elif 'answer_box' in value.keys() and 'snippet_highlighted_words' in value['answer_box'].keys(): 24 | self._value = value['answer_box']["snippet_highlighted_words"][0] 25 | elif 'organic_results' in value and 'snippet' in value["organic_results"][0].keys(): 26 | self._value = value["organic_results"][0]['snippet'] 27 | else: 28 | self._value = value 29 | 30 | if 'organic_results' in value.keys(): 31 | self.results = value['organic_results'] 32 | if len(self.results) > 0: 33 | self.links = [r['link'] for r in self.results] 34 | else: 35 | self.links = [] 36 | else: 37 | self.results = [] 38 | self.links = [] 39 | 40 | def __str__(self) -> str: 41 | json_str = json.dumps(self.raw, indent=2) 42 | return json_str 43 | 44 | def _repr_html_(self) -> str: 45 | json_str = json.dumps(self.raw, indent=2) 46 | return json_str 47 | 48 | 49 | class SerpApiEngine(Engine): 50 | def __init__(self): 51 | super().__init__() 52 | self.config = SYMAI_CONFIG 53 | self.api_key = self.config['SEARCH_ENGINE_API_KEY'] 54 | self.engine = self.config['SEARCH_ENGINE_MODEL'] 55 | self.name = self.__class__.__name__ 56 | 57 | def id(self) -> str: 58 | if self.config.get('SEARCH_ENGINE_API_KEY') and self.config.get('SEARCH_ENGINE_MODEL') == "google": # only support Google for now 59 | if GoogleSearch is None: 60 | CustomUserWarning('SerpApi is not installed. Please install it with `pip install symbolicai[serpapi]`') 61 | return 'search' 62 | return super().id() # default to unregistered 63 | 64 | def command(self, *args, **kwargs): 65 | super().command(*args, **kwargs) 66 | if 'SEARCH_ENGINE_API_KEY' in kwargs: 67 | self.api_key = kwargs['SEARCH_ENGINE_API_KEY'] 68 | if 'SEARCH_ENGINE_MODEL' in kwargs: 69 | self.engine = kwargs['SEARCH_ENGINE_MODEL'] 70 | 71 | def forward(self, argument): 72 | queries = argument.prop.prepared_input 73 | kwargs = argument.kwargs 74 | queries_ = queries if isinstance(queries, list) else [queries] 75 | rsp = [] 76 | engine = kwargs['engine'] if 'engine' in kwargs else self.engine 77 | 78 | for q in queries_: 79 | query = { 80 | "api_key": self.api_key, 81 | "engine": engine, 82 | "q": q, 83 | "google_domain": "google.com", 84 | "gl": "us", 85 | "hl": "en" 86 | } 87 | 88 | # send to Google 89 | with io.capture_output() as captured: # disables prints from GoogleSearch 90 | search = GoogleSearch(query) 91 | res = search.get_dict() 92 | 93 | toret = SearchResult(res) 94 | rsp.append(toret) 95 | 96 | metadata = {} 97 | 98 | output = rsp if isinstance(queries, list) else rsp[0] 99 | output = [output] 100 | return output, metadata 101 | 102 | def prepare(self, argument): 103 | res = '' 104 | res += str(argument.prop.query) 105 | res += str(argument.prop.processed_input) 106 | argument.prop.prepared_input = res 107 | -------------------------------------------------------------------------------- /symai/backend/engines/symbolic/engine_wolframalpha.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from box import Box 4 | 5 | from typing import Optional 6 | 7 | from ...base import Engine 8 | from ...settings import SYMAI_CONFIG 9 | from ....symbol import Result 10 | 11 | try: 12 | import wolframalpha as wa 13 | except: 14 | wa = None 15 | 16 | 17 | class WolframResult(Result): 18 | def __init__(self, value) -> None: 19 | super().__init__(value) 20 | self.raw = Box(value) 21 | self._value = value 22 | 23 | 24 | class WolframAlphaEngine(Engine): 25 | def __init__(self, api_key: Optional[str] = None): 26 | super().__init__() 27 | self.config = SYMAI_CONFIG 28 | self.api_key = self.config['SYMBOLIC_ENGINE_API_KEY'] if api_key is None else api_key 29 | self.client = None 30 | self.name = self.__class__.__name__ 31 | 32 | def id(self) -> str: 33 | if self.config['SYMBOLIC_ENGINE_API_KEY']: 34 | if wa is None: 35 | print('WolframAlpha is not installed. Please install it with `pip install symbolicai[wolframalpha]`') 36 | return 'symbolic' 37 | return super().id() # default to unregistered 38 | 39 | def command(self, *args, **kwargs): 40 | super().command(*args, **kwargs) 41 | if 'SYMBOLIC_ENGINE_API_KEY' in kwargs: 42 | self.api_key = kwargs['SYMBOLIC_ENGINE_API_KEY'] 43 | self.client = wa.Client(self.api_key) if len(self.api_key) > 0 else None 44 | 45 | def forward(self, argument): 46 | queries = argument.prop.prepared_input 47 | 48 | if self.client is None: 49 | self.client = wa.Client(self.api_key) if len(self.api_key) > 0 else None 50 | 51 | queries_ = queries if isinstance(queries, list) else [queries] 52 | rsp = [] 53 | 54 | rsp = self.client.query(queries) 55 | rsp = WolframResult(rsp) 56 | 57 | metadata = {} 58 | if 'metadata' in argument.kwargs and argument.kwargs['metadata']: 59 | metadata['kwargs'] = argument.kwargs 60 | metadata['input'] = queries_ 61 | metadata['output'] = rsp 62 | 63 | return [rsp], metadata 64 | 65 | def prepare(self, argument): 66 | argument.prop.prepared_input = str(argument.prop.processed_input) 67 | -------------------------------------------------------------------------------- /symai/backend/engines/text_to_speech/engine_openai.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from openai import OpenAI 4 | from typing import Optional 5 | 6 | # suppress openai logging 7 | logging.getLogger("openai").setLevel(logging.WARNING) 8 | 9 | from ...base import Engine 10 | from ...settings import SYMAI_CONFIG 11 | from ....symbol import Result 12 | 13 | 14 | class TTSEngine(Engine): 15 | def __init__(self, api_key: Optional[str] = None, model: Optional[str] = None): 16 | super().__init__() 17 | self.config = SYMAI_CONFIG 18 | self.api_key = self.config['TEXT_TO_SPEECH_ENGINE_API_KEY'] if api_key is None else api_key 19 | self.model_id = self.config['TEXT_TO_SPEECH_ENGINE_MODEL'] if model is None else model 20 | self.tokens = [] 21 | self.text = [] 22 | self.client = OpenAI(api_key=self.api_key) 23 | self.name = self.__class__.__name__ 24 | 25 | def id(self) -> str: 26 | if self.config['TEXT_TO_SPEECH_ENGINE_API_KEY']: 27 | return 'text-to-speech' 28 | return super().id() # default to unregistered 29 | 30 | def command(self, *args, **kwargs): 31 | super().command(*args, **kwargs) 32 | if 'TEXT_TO_SPEECH_ENGINE_API_KEY' in kwargs: 33 | self.api_key = kwargs['TEXT_TO_SPEECH_ENGINE_API_KEY'] 34 | if 'TEXT_TO_SPEECH_ENGINE_MODEL' in kwargs: 35 | self.model_id = kwargs['TEXT_TO_SPEECH_ENGINE_MODEL'] 36 | 37 | def forward(self, argument): 38 | kwargs = argument.kwargs 39 | voice, path, prompt = argument.prop.prepared_input 40 | 41 | rsp = self.client.audio.speech.create( 42 | model=self.model_id, 43 | voice=voice, 44 | input=prompt 45 | ) 46 | 47 | metadata = {} 48 | 49 | rsp.stream_to_file(path) 50 | 51 | rsp = Result(rsp) 52 | return [rsp], metadata 53 | 54 | def prepare(self, argument): 55 | assert not argument.prop.processed_input, "TTSEngine does not support processed_input." 56 | assert 'voice' in argument.kwargs, "TTS requires voice selection." 57 | assert 'path' in argument.kwargs, "TTS requires path selection." 58 | voice = str(argument.kwargs['voice']).lower() 59 | audio_file = str(argument.kwargs['path']) 60 | prompt = str(argument.prop.prompt) 61 | argument.prop.prepared_input = (voice, audio_file, prompt) 62 | -------------------------------------------------------------------------------- /symai/backend/engines/text_vision/engine_clip.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import requests 3 | 4 | import torch 5 | from typing import Optional 6 | from PIL import Image 7 | from io import BytesIO 8 | from transformers import CLIPModel, CLIPProcessor 9 | 10 | from ...base import Engine 11 | from ...settings import SYMAI_CONFIG 12 | 13 | 14 | # supress warnings 15 | logging.getLogger("PIL").setLevel(logging.WARNING) 16 | 17 | 18 | class CLIPEngine(Engine): 19 | def __init__(self, model: Optional[str] = None): 20 | super().__init__() 21 | self.model = None # lazy loading 22 | self.preprocessor = None # lazy loading 23 | self.config = SYMAI_CONFIG 24 | self.model_id = self.config['VISION_ENGINE_MODEL'] if model is None else model 25 | self.old_model_id = self.config['VISION_ENGINE_MODEL'] if model is None else model 26 | self.name = self.__class__.__name__ 27 | 28 | def id(self) -> str: 29 | if self.config['VISION_ENGINE_MODEL']: 30 | return 'text_vision' 31 | return super().id() # default to unregistered 32 | 33 | def command(self, *args, **kwargs): 34 | super().command(*args, **kwargs) 35 | if 'VISION_ENGINE_MODEL' in kwargs: 36 | self.model_id = kwargs['VISION_ENGINE_MODEL'] 37 | 38 | def load_images(self, image): 39 | images = [] 40 | if not isinstance(image, (list, tuple)): 41 | image = [image] 42 | 43 | for img in image: 44 | if isinstance(img, bytes): 45 | images.append(Image.open(BytesIO(img))) 46 | elif isinstance(img, str): 47 | if img.startswith('http'): 48 | image_ = requests.get(img, stream=True).raw 49 | else: 50 | image_ = img 51 | image = Image.open(image_) 52 | images.append(image) 53 | return images 54 | 55 | def forward(self, argument): 56 | image_url, text = argument.prop.prepared_input 57 | 58 | if self.model is None or self.model_id != self.old_model_id: 59 | self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") 60 | self.model = CLIPModel.from_pretrained(self.model_id).to(self.device) 61 | self.processor = CLIPProcessor.from_pretrained(self.model_id) 62 | self.old_model_id = self.model_id 63 | 64 | if text is None and image_url is not None: 65 | image = self.load_images(image_url) 66 | inputs = self.processor(images=image, return_tensors="pt").to(self.device) 67 | rsp = self.model.get_image_features(**inputs) 68 | elif image_url is None and text is not None: 69 | inputs = self.processor(text=text, return_tensors="pt").to(self.device) 70 | rsp = self.model.get_text_features(**inputs) 71 | elif image_url is not None and text is not None: 72 | image = self.load_images(image_url) 73 | inputs = self.processor(text=text, images=image, return_tensors="pt", padding=True).to(self.device) 74 | outputs = self.model(**inputs) 75 | logits_per_image = outputs.logits_per_image # this is the image-text similarity score 76 | rsp = logits_per_image.softmax(dim=1) # we can take the softmax to get the label probabilities 77 | else: 78 | raise NotImplementedError("CLIPEngine requires either image or text input.") 79 | 80 | rsp = rsp.squeeze().detach().cpu().numpy() 81 | 82 | metadata = {} 83 | 84 | return [rsp], metadata 85 | 86 | def prepare(self, argument): 87 | assert not argument.prop.processed_input, "CLIPEngine does not support processed_input." 88 | kwargs = argument.kwargs 89 | image_url = argument.kwargs['image'] if 'image' in kwargs else None 90 | text = argument.kwargs['text'] if 'text' in kwargs else None 91 | argument.prop.prepared_input = (image_url, text) 92 | -------------------------------------------------------------------------------- /symai/backend/engines/userinput/engine_console.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from ...base import Engine 4 | 5 | 6 | class UserInputEngine(Engine): 7 | def __init__(self): 8 | super().__init__() 9 | self.name = self.__class__.__name__ 10 | 11 | def id(self) -> str: 12 | return 'userinput' 13 | 14 | def forward(self, argument): 15 | msg = argument.prop.prepared_input 16 | kwargs = argument.kwargs 17 | 18 | mock = kwargs['mock'] if 'mock' in kwargs else False 19 | if mock: # mock user input 20 | print(msg, end='') # print prompt 21 | rsp = mock 22 | else: 23 | rsp = input(msg) 24 | 25 | metadata = {} 26 | 27 | return [rsp], metadata 28 | 29 | def prepare(self, argument): 30 | # here the prompt marks the user input message 31 | argument.prop.prepared_input = str(argument.prop.processed_input) 32 | -------------------------------------------------------------------------------- /symai/backend/mixin/__init__.py: -------------------------------------------------------------------------------- 1 | from .anthropic import SUPPORTED_CHAT_MODELS as ANTHROPIC_CHAT_MODELS 2 | from .anthropic import SUPPORTED_REASONING_MODELS as ANTHROPIC_REASONING_MODELS 3 | from .deepseek import SUPPORTED_CHAT_MODELS as DEEPSEEK_CHAT_MODELS 4 | from .deepseek import SUPPORTED_REASONING_MODELS as DEEPSEEK_REASONING_MODELS 5 | from .google import SUPPORTED_CHAT_MODELS as GOOGLE_CHAT_MODELS 6 | from .google import SUPPORTED_REASONING_MODELS as GOOGLE_REASONING_MODELS 7 | from .openai import SUPPORTED_CHAT_MODELS as OPENAI_CHAT_MODELS 8 | from .openai import SUPPORTED_REASONING_MODELS as OPENAI_REASONING_MODELS 9 | -------------------------------------------------------------------------------- /symai/backend/mixin/anthropic.py: -------------------------------------------------------------------------------- 1 | # https://docs.anthropic.com/en/docs/about-claude/models 2 | SUPPORTED_CHAT_MODELS = [ 3 | 'claude-3-5-sonnet-latest', 4 | 'claude-3-5-haiku-latest', 5 | 'claude-3-5-sonnet-20241022', 6 | 'claude-3-5-sonnet-20240620', 7 | 'claude-3-opus-latest', 8 | 'claude-3-opus-20240229', 9 | 'claude-3-sonnet-20240229', 10 | 'claude-3-haiku-20240307', 11 | ] 12 | SUPPORTED_REASONING_MODELS = [ 13 | "claude-opus-4-0", 14 | "claude-sonnet-4-0", 15 | 'claude-3-7-sonnet-latest', 16 | ] 17 | 18 | class AnthropicMixin: 19 | def api_max_context_tokens(self): 20 | if self.model == 'claude-opus-4-0' or \ 21 | self.model == 'claude-sonnet-4-0' or \ 22 | self.model == 'claude-3-7-sonnet-latest' or \ 23 | self.model == 'claude-3-5-sonnet-latest' or \ 24 | self.model == 'claude-3-5-sonnet-20241022' or \ 25 | self.model == 'claude-3-5-sonnet-20240620' or \ 26 | self.model == 'claude-3-opus-latest' or \ 27 | self.model == 'claude-3-opus-20240229' or \ 28 | self.model == 'claude-3-sonnet-20240229' or \ 29 | self.model == 'claude-3-haiku-20240307': 30 | return 200_000 31 | 32 | def api_max_response_tokens(self): 33 | if self.model == 'claude-sonnet-4-0' or \ 34 | self.model == 'claude-3-7-sonnet-latest': 35 | return 64_000 36 | if self.model == 'claude-opus-4-0': 37 | return 32_000 38 | if self.model == 'claude-3-5-sonnet-latest' or \ 39 | self.model == 'claude-3-5-sonnet-20241022' or \ 40 | self.model == 'claude-3-5-haiku-latest': 41 | return 8_192 42 | if self.model == 'claude-3-5-sonnet-20240620' or \ 43 | self.model == 'claude-3-opus-latest' or \ 44 | self.model == 'clade-3-opus-20240229' or \ 45 | self.model == 'claude-3-sonnet-20240229' or \ 46 | self.model == 'claude-3-haiku-20240307': 47 | return 4_096 48 | -------------------------------------------------------------------------------- /symai/backend/mixin/deepseek.py: -------------------------------------------------------------------------------- 1 | # https://api-docs.deepseek.com/quick_start/pricing 2 | SUPPORTED_CHAT_MODELS = [] 3 | SUPPORTED_REASONING_MODELS = [ 4 | 'deepseek-reasoner' 5 | ] 6 | 7 | class DeepSeekMixin: 8 | def api_max_context_tokens(self): 9 | if self.model == 'deepseek-reasoner': 10 | return 64_000 11 | 12 | def api_max_response_tokens(self): 13 | if self.model == 'deepseek-reasoner': 14 | return 8_000 15 | -------------------------------------------------------------------------------- /symai/backend/mixin/google.py: -------------------------------------------------------------------------------- 1 | # https://ai.google.dev/gemini-api/docs/models/gemini 2 | SUPPORTED_CHAT_MODELS = [] 3 | SUPPORTED_REASONING_MODELS = [ 4 | 'gemini-2.5-pro-preview-05-06', 5 | 'gemini-2.5-flash-preview-05-20', 6 | ] 7 | 8 | class GoogleMixin: 9 | def api_max_context_tokens(self): 10 | if self.model.startswith('gemini-2.5-'): 11 | return 1_048_576 12 | 13 | def api_max_response_tokens(self): 14 | if self.model == 'gemini-2.5-': 15 | return 65_536 16 | -------------------------------------------------------------------------------- /symai/backend/settings.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import sys 4 | from pathlib import Path 5 | 6 | 7 | class SymAIConfig: 8 | """Manages SymbolicAI configuration files across different environments. 9 | Configuration Priority: 10 | 1. Debug mode (current working directory) 11 | 2. Python environment-specific config 12 | 3. User home directory config 13 | """ 14 | 15 | def __init__(self): 16 | """Initialize configuration paths based on current Python environment.""" 17 | self._env_path = Path(sys.prefix) 18 | self._env_config_dir = self._env_path / '.symai' 19 | self._home_config_dir = Path.home() / '.symai' 20 | self._debug_dir = Path.cwd() # Current working directory for debug mode 21 | 22 | @property 23 | def config_dir(self) -> Path: 24 | """Returns the active configuration directory based on priority system.""" 25 | # Debug mode takes precedence 26 | if (self._debug_dir / 'symai.config.json').exists(): 27 | return self._debug_dir 28 | # Then environment config 29 | if self._env_config_dir.exists(): 30 | return self._env_config_dir 31 | # Finally home directory 32 | return self._home_config_dir 33 | 34 | def get_config_path(self, filename: str, fallback_to_home: bool = False) -> Path: 35 | """Gets the config path using the priority system or forces fallback to home.""" 36 | debug_config = self._debug_dir / filename 37 | env_config = self._env_config_dir / filename 38 | home_config = self._home_config_dir / filename 39 | 40 | # Check debug first (only valid for symai.config.json) 41 | if filename == 'symai.config.json' and debug_config.exists(): 42 | return debug_config 43 | 44 | # If forced to fallback, return home config if it exists, otherwise environment 45 | if fallback_to_home: 46 | return home_config if home_config.exists() else env_config 47 | 48 | # Normal priority-based resolution 49 | # If environment config doesn't exist, return that path (for creation) 50 | if not env_config.exists(): 51 | return env_config 52 | # Otherwise use environment config 53 | return env_config 54 | 55 | def load_config(self, filename: str, fallback_to_home: bool = False) -> dict: 56 | """Loads JSON data from the determined config location.""" 57 | config_path = self.get_config_path(filename, fallback_to_home=fallback_to_home) 58 | if not config_path.exists(): 59 | return {} 60 | with open(config_path, 'r', encoding='utf-8') as f: 61 | return json.load(f) 62 | 63 | def save_config(self, filename: str, data: dict, fallback_to_home: bool = False) -> None: 64 | """Saves JSON data to the determined config location.""" 65 | config_path = self.get_config_path(filename, fallback_to_home=fallback_to_home) 66 | os.makedirs(config_path.parent, exist_ok=True) 67 | with open(config_path, 'w', encoding='utf-8') as f: 68 | json.dump(data, f, indent=4) 69 | 70 | def migrate_config(self, filename: str, updates: dict) -> None: 71 | """Updates existing configuration with new fields.""" 72 | config = self.load_config(filename) 73 | config.update(updates) 74 | self.save_config(filename, config) 75 | 76 | SYMAI_CONFIG = {} 77 | SYMSH_CONFIG = {} 78 | SYMSERVER_CONFIG = {} 79 | HOME_PATH = Path(SymAIConfig().config_dir) 80 | -------------------------------------------------------------------------------- /symai/collect/__init__.py: -------------------------------------------------------------------------------- 1 | from .pipeline import CollectionRepository, rec_serialize 2 | from .dynamic import create_object_from_string -------------------------------------------------------------------------------- /symai/constraints.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from .exceptions import ConstraintViolationException, InvalidPropertyException 4 | from .symbol import Symbol 5 | 6 | 7 | class DictFormatConstraint: 8 | def __init__(self, format=None): 9 | if isinstance(format, str): 10 | self.format = json.loads(format) 11 | elif isinstance(format, dict): 12 | self.format = format 13 | else: 14 | raise InvalidPropertyException(f"Unsupported format type: {type(format)}") 15 | 16 | def __call__(self, input: Symbol): 17 | input = Symbol(input) 18 | if input.value_type == str: 19 | try: 20 | gen_dict = json.loads(input.value) 21 | except json.JSONDecodeError as e: 22 | raise ConstraintViolationException(f"Invalid JSON: ```json\n{input.value}\n```\n{e}") 23 | return DictFormatConstraint.check_keys(self.format, gen_dict) 24 | elif input.value_type == dict: 25 | return DictFormatConstraint.check_keys(self.format, input.value) 26 | else: 27 | raise ConstraintViolationException(f"Unsupported input type: {input.value_type}") 28 | 29 | @staticmethod 30 | def check_keys(json_format, gen_dict): 31 | for key, value in json_format.items(): 32 | if not str(key).startswith('{') and not str(key).endswith('}') and \ 33 | key not in gen_dict or not isinstance(gen_dict[key], type(value)): 34 | raise ConstraintViolationException(f"Key `{key}` not found or type `{type(key)}` mismatch") 35 | if isinstance(gen_dict[key], dict): 36 | # on a dictionary, descend recursively 37 | return DictFormatConstraint.check_keys(value, gen_dict[key]) 38 | return True 39 | -------------------------------------------------------------------------------- /symai/endpoints/__init__py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ExtensityAI/symbolicai/a55724b5bff3b336af3543f6b54632d8de07b678/symai/endpoints/__init__py -------------------------------------------------------------------------------- /symai/exceptions.py: -------------------------------------------------------------------------------- 1 | class ConstraintViolationException(Exception): 2 | pass 3 | 4 | 5 | class InvalidPropertyException(Exception): 6 | pass 7 | 8 | 9 | class TemplatePropertyException(Exception): 10 | pass 11 | -------------------------------------------------------------------------------- /symai/extended/__init__.py: -------------------------------------------------------------------------------- 1 | from .arxiv_pdf_parser import * 2 | from .conversation import * 3 | from .crawler import * 4 | from .document import * 5 | from .file_merger import * 6 | from .graph import * 7 | from .html_style_template import * 8 | from .packages import * 9 | from .repo_cloner import * 10 | from .solver import * 11 | from .summarizer import * 12 | from .personas import * 13 | from .api_builder import APIBuilder, APIExecutor 14 | from .os_command import OSCommand 15 | from .taypan_interpreter import TaypanInterpreter 16 | from .bibtex_parser import BibTexParser 17 | from .vectordb import VectorDB -------------------------------------------------------------------------------- /symai/extended/arxiv_pdf_parser.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import shutil 4 | import requests 5 | 6 | from concurrent.futures import ThreadPoolExecutor, as_completed 7 | 8 | from ..symbol import Expression, Symbol 9 | from .file_merger import FileMerger 10 | from ..backend.settings import HOME_PATH 11 | 12 | 13 | class ArxivPdfParser(Expression): 14 | def __init__(self, url_pattern: str = r'https://arxiv.org/(?:pdf|abs)/(\d+.\d+)(?:\.pdf)?', **kwargs): 15 | super().__init__(**kwargs) 16 | self.url_pattern = url_pattern 17 | self.merger = FileMerger() 18 | 19 | def forward(self, data: Symbol, **kwargs) -> Symbol: 20 | # Extract all urls from the data 21 | urls = re.findall(self.url_pattern, str(data)) 22 | 23 | # Convert all urls to pdf urls 24 | pdf_urls = [f"https://arxiv.org/pdf/" + (f"{url.split('/')[-1]}.pdf" if 'pdf' not in url else {url.split('/')[-1]}) for url in urls] 25 | 26 | # Create temporary folder in the home directory 27 | output_path = os.path.join(HOME_PATH, "temp/downloads") 28 | os.makedirs(output_path, exist_ok=True) 29 | 30 | pdf_files = [] 31 | with ThreadPoolExecutor() as executor: 32 | # Download all pdfs in parallel 33 | future_to_url = {executor.submit(self.download_pdf, url, output_path): url for url in pdf_urls} 34 | for future in as_completed(future_to_url): 35 | url = future_to_url[future] 36 | try: 37 | pdf_files.append(future.result()) 38 | except Exception as exc: 39 | print('%r generated an exception: %s' % (url, exc)) 40 | 41 | if len(pdf_files) == 0: 42 | return None 43 | 44 | # Merge all pdfs into one file 45 | merged_file = self.merger(output_path, **kwargs) 46 | 47 | # Return the merged file as a Symbol 48 | return_file = self._to_symbol(merged_file) 49 | 50 | # Delete the temporary folder after merging the files 51 | shutil.rmtree(output_path) 52 | 53 | return return_file 54 | 55 | def download_pdf(self, url, output_path): 56 | # Download pdfs 57 | response = requests.get(url) 58 | file = os.path.join(output_path, f'{url.split("/")[-1]}') 59 | with open(file, 'wb') as f: 60 | f.write(response.content) 61 | return file 62 | -------------------------------------------------------------------------------- /symai/extended/bibtex_parser.py: -------------------------------------------------------------------------------- 1 | from .. import core 2 | from ..pre_processors import PreProcessor 3 | from ..symbol import Expression, Symbol 4 | from ..post_processors import CodeExtractPostProcessor 5 | 6 | 7 | BIB_DESCRIPTION = """[Description] 8 | You take in a text with references to papers and return a list of biblatex entries. 9 | The cite reference is always having the last name of the author and then separated by a colon the year of publication, and optionally if multiple papers are published in the same year, a letter is appended to the year of publication, i.e. Agarwal:20a, Agarwal:20b, etc. 10 | Exclude URLs from the biblatex entries. 11 | 12 | [Single Example] 13 | >>> 14 | An Optimistic Perspective on Offline Reinforcement Learning, [Submitted on 10 Jul 2019 (v1), last revised 22 Jun 2020 (this version, v4)] Rishabh Agarwal, Dale Schuurmans, Mohammad Norouzi, https://arxiv.org/abs/1907.04543, arXiv:1907.04543 [cs.LG] 15 | <<< 16 | ```bibtex 17 | @article{Agarwal:20, 18 | title={An Optimistic Perspective on Offline Reinforcement Learning}, 19 | author={R. Agarwal and D. Schuurmans and M. Norouzi}, 20 | year={2020}, 21 | journal={arXiv preprint arXiv:1907.04543}, 22 | howpublished={arXiv}, 23 | primaryClass={cs.LG} 24 | } 25 | ``` 26 | 27 | [Multiple Examples] 28 | >>> 29 | Benchmarking Batch Deep Reinforcement Learning Algorithms, [Submitted on 3 Oct 2019] Scott Fujimoto, Edoardo Conti, Mohammad Ghavamzadeh, Joelle Pineau arXiv:1910.01708 [cs.LG] 30 | 31 | Off-Policy Deep Reinforcement Learning without Exploration, Scott Fujimoto, D. Meger, Doina Precup 32 | Published in International Conference on… 7 December 2018 arXiv:1812.02900v3 [cs.LG] 10 Aug 2019 33 | 34 | Multimodal Few-Shot Learning with Frozen Language Models Maria Tsimpoukelli 35 | ~Maria_Tsimpoukelli1 36 | , Jacob Menick, Serkan Cabi, S. M. Ali Eslami, Oriol Vinyals, Felix Hill Published: 09 Nov 2021, Last Modified: 22 Oct 2023 NeurIPS 2021 Poster Advances in Neural Information Processing Systems 34 (NeurIPS 2021), pages 200--212 37 | 38 | <<< 39 | ```bibtex 40 | @article{Fujimoto:19a, 41 | title={Benchmarking Batch Deep Reinforcement Learning Algorithms}, 42 | author={S. Fujimoto and E. Conti and M. Ghavamzadeh and J. Pineau}, 43 | year={2019}, 44 | journal={arXiv preprint arXiv:1910.01708}, 45 | howpublished={arXiv}, 46 | primaryClass={cs.LG} 47 | } 48 | 49 | @article{Fujimoto:19b, 50 | title={Off-Policy Deep Reinforcement Learning without Exploration}, 51 | author={S. Fujimoto and D. Meger and D. Precup}, 52 | year={2019}, 53 | journal={arXiv preprint arXiv:1812.02900}, 54 | howpublished={arXiv}, 55 | primaryClass={cs.LG} 56 | } 57 | 58 | @inproceedings{tsimpoukelli:21, 59 | title = {Multimodal {Few}-{Shot} {Learning} with {Frozen} {Language} {Models}}, 60 | booktitle = {Advances in {Neural} {Information} {Processing} {Systems} 34: {Annual} {Conference} on {Neural} {Information} {Processing} {Systems} 2021, {NeurIPS} 2021, {December} 6-14, 2021, virtual}, 61 | author = {M. Tsimpoukelli and J. Menick and S. Cabi and S. M. A. Eslami and O. Vinyals and F. Hill}, 62 | editor = {M.'A. Ranzato and A. Beygelzimer and Y. N. Dauphin and P. Liang and J. W. Vaughan}, 63 | year = {2021}, 64 | pages = {200--212}, 65 | } 66 | ``` 67 | """ 68 | 69 | 70 | class BibTexPreProcessor(PreProcessor): 71 | def __call__(self, argument): 72 | return '>>>\n{}\n\n<<<\n'.format(str(argument.args[0])) 73 | 74 | 75 | class BibTexParser(Expression): 76 | @property 77 | def static_context(self) -> str: 78 | return BIB_DESCRIPTION 79 | 80 | def __init__(self, **kwargs): 81 | super().__init__(**kwargs) 82 | self.sym_return_type = BibTexParser 83 | 84 | def forward(self, sym: Symbol, **kwargs) -> Symbol: 85 | @core.zero_shot(prompt="Create bibtex entries:\n", 86 | pre_processors=[BibTexPreProcessor()], 87 | post_processors=[CodeExtractPostProcessor()], **kwargs) 88 | def _func(_, text) -> str: 89 | pass 90 | return _func(self, sym) 91 | -------------------------------------------------------------------------------- /symai/extended/crawler.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from ..components import Clean, Sequence, Stream 4 | from ..symbol import Expression, Symbol 5 | from ..interfaces import Interface 6 | 7 | 8 | class Crawler(Expression): 9 | def __init__(self, filters: List[Expression] = [], **kwargs): 10 | super().__init__(**kwargs) 11 | filters = filters if isinstance(filters, List) or isinstance(filters, tuple) else [filters] 12 | self.crawler = Interface('selenium') 13 | self.data_stream = Stream(Sequence( 14 | Clean(), 15 | *filters 16 | )) 17 | 18 | def forward(self, url: str, pattern='www', **kwargs) -> Symbol: 19 | res = self.crawler(url=url, pattern=pattern, **kwargs) 20 | vals = list(self.data_stream(res, **kwargs)) 21 | return Symbol(vals) 22 | -------------------------------------------------------------------------------- /symai/extended/document.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from typing import Callable, List, Optional, Union 4 | 5 | from ..components import FileReader, Indexer 6 | from ..formatter import ParagraphFormatter 7 | from ..symbol import Expression, Symbol 8 | 9 | 10 | class DocumentRetriever(Expression): 11 | def __init__( 12 | self, 13 | source: Optional[str] = None, 14 | *, 15 | index_name: str = Indexer.DEFAULT, 16 | top_k: int = 5, 17 | max_depth: int = 1, 18 | formatter: Callable = ParagraphFormatter(), 19 | overwrite: bool = False, 20 | with_metadata: bool = False, 21 | raw_result: Optional[bool] = False, 22 | new_dim: Optional[int] = None, 23 | **kwargs 24 | ): 25 | super().__init__(**kwargs) 26 | self.indexer = Indexer(index_name=index_name, top_k=top_k, formatter=formatter, auto_add=False, new_dim=new_dim) 27 | self.reader = FileReader(with_metadata=with_metadata) 28 | self.new_dim = new_dim 29 | 30 | if overwrite: 31 | self.config(None, purge=True, index_name=self.indexer.index_name, **kwargs) 32 | 33 | # we insert the text into the index if (1) index does not exist and (2) there's a specific source 34 | if source is not None and not self.indexer.exists(): 35 | self.indexer.register() 36 | text = self.parse_source(source, with_metadata=with_metadata, max_depth=max_depth, **kwargs) 37 | self.index = self.indexer(data=text, raw_result=raw_result, **kwargs) 38 | else: 39 | # we don't insert the text at initialization since the index already exists and there's no specific source 40 | self.index = self.indexer(raw_result=raw_result, **kwargs) 41 | 42 | def forward( 43 | self, 44 | query: Symbol, 45 | raw_result: Optional[bool] = False, 46 | ) -> Symbol: 47 | return self.index( 48 | query, 49 | raw_result=raw_result, 50 | ) 51 | 52 | def insert(self, source: Union[str, Path], **kwargs): 53 | # dynamically insert data into the index given a session 54 | # the data can be: 55 | # - a string (e.g. something that the user wants to insert) 56 | # - a file path (e.g. a new file that the user wants to insert) 57 | # - a directory path (e.g. a new directory that the user wants to insert) 58 | text = self.parse_source(source, with_metadata=kwargs.get('with_metadata', False), max_depth=kwargs.get('max_depth', 1), **kwargs) 59 | #NOTE: Do we need `new_dim` here? 60 | self.add(text, index_name=self.indexer.index_name, **kwargs) 61 | self.config(None, save=True, index_name=self.indexer.index_name, **kwargs) 62 | 63 | def parse_source(self, source: str, with_metadata: bool, max_depth: int, **kwargs) -> List[Union[str, 'TextContainer']]: 64 | maybe_path = Path(source) 65 | if isinstance(source, str) and not (maybe_path.is_file() or maybe_path.is_dir()): 66 | return Symbol(source).zip(new_dim=self.new_dim) 67 | if maybe_path.is_dir(): 68 | files = FileReader.get_files(source, max_depth) 69 | return self.reader(files, with_metadata=with_metadata, **kwargs) 70 | if maybe_path.is_file(): 71 | return self.reader(source, with_metadata=with_metadata, **kwargs) 72 | raise ValueError(f"Invalid source: {source}; must be a file, directory, or string") 73 | -------------------------------------------------------------------------------- /symai/extended/file_merger.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from tqdm import tqdm 4 | from typing import List 5 | 6 | from ..symbol import Expression, Symbol 7 | from ..components import FileReader 8 | 9 | 10 | class FileMerger(Expression): 11 | """ 12 | Class to merge contents of multiple files into one, specified by their file endings and root path. 13 | Files specified in the exclude list will not be included. 14 | """ 15 | def __init__(self, file_endings: List[str] = ['.py', '.md', '.txt', '.sh', '.pdf', '.json', '.yaml', '.java', '.cpp', '.hpp', '.c', '.h', '.js', '.css', '.html', '.xml', '.csv', '.tsv', '.yml', '.rst', '.ipynb', '.tex', '.bib'], 16 | file_excludes: List[str] = ['__init__.py', '__pycache__', 'LICENSE', 'requirements.txt', 'environment.yaml', '.git'], **kwargs): 17 | super().__init__(**kwargs) 18 | self.file_endings = file_endings 19 | self.file_excludes = file_excludes 20 | self.reader = FileReader() 21 | 22 | def forward(self, root_path: str, **kwargs) -> Symbol: 23 | """ 24 | Method to find, read, merge and return contents of files in the form of a Symbol starting from the root_path. 25 | 26 | The method recursively searches files with specified endings from the root path, excluding specific file names. 27 | Then, it reads all found files using the FileReader, merges them into one file (merged_file), and returns the 28 | merged file as a Symbol. 29 | """ 30 | merged_file = "" 31 | 32 | # Implement recursive file search 33 | # use tqdm for progress bar and description 34 | tqdm_desc = f"Reading file: ..." 35 | # use os.walk to recursively search for files in the root path 36 | progress = tqdm(os.walk(root_path), desc=tqdm_desc) 37 | 38 | for root, dirs, files in progress: 39 | for file in files: 40 | file_path = os.path.join(root, file) 41 | # Exclude files with the specified names in the path 42 | if any(exclude in file_path for exclude in self.file_excludes): 43 | continue 44 | 45 | # Look only for files with the specified endings 46 | if file.endswith(tuple(self.file_endings)): 47 | # Read in the file using the FileReader 48 | file_content = self.reader(file_path, **kwargs).value 49 | 50 | # escape file name spaces 51 | file_path = file_path.replace(" ", "\\ ") 52 | 53 | # Append start and end markers for each file 54 | file_content = f"# ----[FILE_START]{file_path}[FILE_CONTENT]:\n" + \ 55 | file_content + \ 56 | f"\n# ----[FILE_END]{file_path}\n" 57 | 58 | # Merge the file contents 59 | merged_file += file_content 60 | 61 | # Update the progress bar description 62 | tqdm_desc = f"Reading file: {file_path}" 63 | progress.set_description(tqdm_desc) 64 | 65 | # Return the merged file as a Symbol 66 | return self._to_symbol(merged_file) -------------------------------------------------------------------------------- /symai/extended/graph.py: -------------------------------------------------------------------------------- 1 | from multiprocessing import Pool 2 | from typing import Callable 3 | 4 | from .. import core 5 | from ..formatter import SentenceFormatter 6 | from ..post_processors import StripPostProcessor 7 | from ..pre_processors import PreProcessor 8 | from ..prompts import Prompt 9 | from ..symbol import Expression, Symbol 10 | 11 | GRAPH_DESCRIPTION = """[Description] 12 | Build source-target relationship pairs for named entities based for the [DATA] section. The [DATA] section contains one sentence. 13 | Format all extracted pairs in a CSV format with the following columns: source, target, count. 14 | The source is related to the target based on intermediate terms, such as "has a", "has two", "is used to" etc. 15 | If more than one entity pair is extracted from the same sentence, then the CSV file should contain multiple rows separated by a newline (\\n) 16 | """ 17 | 18 | 19 | class GraphPreProcessor(PreProcessor): 20 | def __call__(self, argument): 21 | return '$> {} =>'.format(str(argument.args[0])) 22 | 23 | 24 | class Graph(Expression): 25 | @property 26 | def static_context(self) -> str: 27 | return GRAPH_DESCRIPTION 28 | 29 | def __init__(self, formatter: Callable = SentenceFormatter(), n_workers: int = 1, verbose: bool = False, **kwargs): 30 | super().__init__(**kwargs) 31 | self.formatter = formatter 32 | self.n_workers = n_workers 33 | self.sym_return_type = Graph 34 | self.verbose = verbose 35 | 36 | def process_symbol(self, s, *args, **kwargs): 37 | res = '' 38 | 39 | @core.few_shot(prompt="Extract relationships between entities:\n", 40 | examples=Prompt([ 41 | '$> John has a dog. =>John, dog, 1 EOF', 42 | '$> Karl has two sons. =>Karl, sons, 2 EOF', 43 | '$> Similarly, the term general linguistics is used to distinguish core linguistics from other types of study =>general linguistics, core linguistics, 1 EOF', 44 | '$> X has Y and Z has Y =>X, Y, 1\nZ, Y, 1 EOF', 45 | ]), 46 | pre_processors=[GraphPreProcessor()], 47 | post_processors=[StripPostProcessor()], 48 | stop=['EOF'], **kwargs) 49 | def _func(_, text) -> str: 50 | pass 51 | 52 | if len(str(s)) > 0: 53 | if self.verbose: print(s) 54 | r = _func(self, s) 55 | rec = str(r) 56 | lines = rec.split('\n') 57 | for l in lines: 58 | l = l.strip() 59 | if len(l) > 0: 60 | csv = l.split(',') 61 | try: 62 | if len(csv) == 3 and \ 63 | csv[0].strip() != '' and \ 64 | csv[1].strip() != '' and \ 65 | int(csv[2].strip()) > 0: 66 | res += l + '\n' 67 | except Exception as e: 68 | if self.verbose: print(e) 69 | pass 70 | return res 71 | 72 | def forward(self, sym: Symbol, **kwargs) -> Symbol: 73 | res = 'source,target,value\n' 74 | sym_list = self.formatter(sym).value 75 | if self.n_workers == 1: 76 | for s in sym_list: 77 | res += self.process_symbol(s) 78 | return res 79 | with Pool(self.n_workers) as p: 80 | results = p.map(self.process_symbol, sym_list) 81 | for r in results: 82 | res += r 83 | return res 84 | 85 | 86 | -------------------------------------------------------------------------------- /symai/extended/html_style_template.py: -------------------------------------------------------------------------------- 1 | from ..components import Stream, Style, Template 2 | from ..symbol import Expression, Symbol 3 | 4 | HEADER_STYLE_DESCRIPTION = """[Description] 5 | Design a web view of data with HTML. 6 | Use dark theme and best practices for colors, text font, etc. 7 | Use Bootstrap for styling. 8 | 9 | [Examples] 10 | Chose the appropriate HTML tags: 11 | - Use URL links such as http://www.example.com to: 12 | - Use tag for images: http://www.../image.jpeg 13 | - Use

...

tags for the titles and headers:

...

tags for paragraphs 14 | - Use
    ...
tags for unordered lists ['demo', 'example', ...]:
  • demo
  • example
  • ...
15 | 16 | [Template] 17 | 18 | 19 | 20 | 21 | 22 | Generated HTML Results 23 | 24 | 25 | 26 |
27 |

Rendered results:

28 | 29 | {{placeholder}} 30 |
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