Skip to content

πŸ§ πŸ”— Graph-Based Programmable Neuro-Symbolic LM Framework - a production-first LM framework built with decade old Deep Learning best practices

License

Notifications You must be signed in to change notification settings

SynaLinks/synalinks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Synalinks: A production-first LM framework built with decade old Deep Learning best practices

Beta Coverage Badge Pypi Downloads Discord Python package License: Apache-2.0

What is Synalinks?

Synalinks is an open-source framework that makes it easy to create, evaluate, train, and deploy industry-standard Language Models (LMs) applications. Synalinks follows the principle of progressive disclosure of complexity: meaning that simple workflows should be quick and easy, while arbitrarily advanced ones should be possible via a clear path that builds upon what you've already learned.

Synalinks is an adaptation of Keras 3 focused on neuro-symbolic systems and in-context reinforcement learning, an ensemble of techniques that enhance the LMs predictions and accuracy without changing the weights of the model. The goal of Synalinks is to facilitate the rapid setup of simple applications while providing the flexibility for researchers and advanced users to develop sophisticated systems.

Who is Synalinks for?

Synalinks is designed for a diverse range of users, from professionals and AI researchers to students, independent developers, and hobbyists. It is suitable for anyone who wants to learn about AI by building/composing blocks or build solid foundations for enterprise-grade products. While a background in Machine Learning and Deep Learning can be advantageous β€” as Synalinks leverages design patterns from Keras, one of the most user-friendly and popular Deep Learning frameworks β€” it is not a prerequisite. Synalinks is designed to be accessible to anyone with programming skills in Python, making it a versatile and inclusive platform for AI development.

Why use Synalinks?

Developping a successful LM application in a profesional context, beyond stateless chatbots, is difficult and typically include:

  • Building optimized prompts with examples/hints at each step: Synalinks uses advanced In-Context Reinforcement Learning techniques to optimize each prompt.
  • Pipelines that change over time: Easily edit your pipelines, re-run your training, and you're good to go.
  • Ensuring the correctness of the LMs output: Synalinks combines constrained structured output with In-Context RL to ensure both format and content correctness.
  • Optimizing async processes: Synalinks automatically optimizes your pipelines by detecting parallel processes.
  • Assessing the performance of your application: Synalinks provides built-in metrics and rewards to evaluate your workflows.
  • Configuring Language & Embedding Models: Synalinks uses LiteLLM and comes ready with 100+ integrations out-of-the-box.
  • Documenting your ML workflows: Plot your workflows, training history, and evaluations; document everything.
  • Versioning the prompts/pipelines: Each program is serializable into JSON so you can version it with git.
  • Deploying REST APIs: Compatible out-of-the-box with FastAPI so your Data Scientists and Web Developers can stop tearing each other apart.

Synalinks can help you simplify these tasks by leveraging decade old practices in Deep Learning frameworks. We provide a comprehensive suite of tools and features designed to streamline the development process, making it easier to create, evaluate, train, document and deploy robust neuro-symbolic LMs applications.

Install

pip install synalinks

or (recommended)

uv pip install synalinks

Programming your application: 3 ways

Using the Functional API

You start from Input, you chain modules calls to specify the model's forward pass, and finally, you create your model from inputs and outputs:

import synalinks
import asyncio

async def main():
    class Query(synalinks.DataModel):
        query: str = synalinks.Field(
            description="The user query",
        )

    class AnswerWithThinking(synalinks.DataModel):
        thinking: str = synalinks.Field(
            description="Your step by step thinking process",
        )
        answer: float = synalinks.Field(
            description="The correct numerical answer",
        )

    language_model = synalinks.LanguageModel(model="ollama_chat/deepseek-r1")

    x0 = synalinks.Input(data_model=Query)
    x1 = await synalinks.Generator(
        data_model=AnswerWithThinking,
        language_model=language_model,
    )(x0)

    program = synalinks.Program(
        inputs=x0,
        outputs=x1,
        name="chain_of_thought",
        description="Usefull to answer in a step by step manner.",
    )

if __name__ == "__main__":
    asyncio.run(main())

Subclassing the Program class

In that case, you should define your modules in __init__() and implement the program's structure in call().

Note: you can optionaly have a training argument (boolean), which you can use to specify a different behavior in training and inference.

import synalinks
import asyncio

async def main():
    class Query(synalinks.DataModel):
        query: str = synalinks.Field(
            description="The user query",
        )

    class AnswerWithThinking(synalinks.DataModel):
        thinking: str = synalinks.Field(
            description="Your step by step thinking process",
        )
        answer: float = synalinks.Field(
            description="The correct numerical answer",
        )

    class ChainOfThought(synalinks.Program):
        """Usefull to answer in a step by step manner.
        
        The first line of the docstring is provided as description
        for the program if not provided in the `super().__init__()`.
        In a similar way the name is automatically infered based on
        the class name if not provided.
        """

        def __init__(
            self,
            language_model=None,
            name=None,
            description=None,
            trainable=True,
        ):
            super().__init__(
                name=name,
                description=description,
                trainable=trainable,
            )
            self.answer = synalinks.Generator(
                data_model=AnswerWithThinking,
                language_model=language_model,
                name=self.name+"_generator",
            )

        async def call(self, inputs, training=False):
            x = await self.answer(inputs, training=training)
            return x

        def get_config(self):
            config = {
                "name": self.name,
                "description": self.description,
                "trainable": self.trainable,
            }
            language_model_config = \
            {
                "language_model": synalinks.saving.serialize_synalinks_object(
                    self.language_model
                )
            }
            return {**config, **language_model_config}

        @classmethod
        def from_config(cls, config):
            language_model = synalinks.saving.deserialize_synalinks_object(
                config.pop("language_model")
            )
            return cls(language_model=language_model, **config)

    language_model = synalinks.LanguageModel(model="ollama_chat/deepseek-r1")

    program = ChainOfThought(language_model=language_model)

if __name__ == "__main__":
    asyncio.run(main())

Using the Sequential API

In addition, Sequential is a special case of program where the program is purely a stack of single-input, single-output modules.

import synalinks
import asyncio

async def main():
    class Query(synalinks.DataModel):
        query: str = synalinks.Field(
            description="The user query",
        )

    class AnswerWithThinking(synalinks.DataModel):
        thinking: str = synalinks.Field(
            description="Your step by step thinking process",
        )
        answer: float = synalinks.Field(
            description="The correct numerical answer",
        )

    language_model = synalinks.LanguageModel(model="ollama_chat/deepseek-r1")

    program = synalinks.Sequential(
        [
            synalinks.Input(
                data_model=Query,
            ),
            synalinks.Generator(
                data_model=AnswerWithThinking,
                language_model=language_model,
            ),
        ],
        name="chain_of_thought",
        description="Usefull to answer in a step by step manner.",
    )

if __name__ == "__main__":
    asyncio.run(main())

Getting summary of your program

To print a tabular summary of your program:

program.summary()

Or a plot (usefull to document your system):

synalinks.utils.plot_program(
    program,
    show_module_names=True,
    show_trainable=True,
    show_schemas=True,
)

chain_of_thought

Running your program

To run your program use the following:

result = await program(
    Query(query="What is the French city of aerospace?"),
)

Training your program

async def main():

    # ... your program definition

    (x_train, y_train), (x_test, y_test) = synalinks.datasets.gsm8k.load_data()

    program.compile(
        reward=synalinks.rewards.ExactMatch(in_mask=["answer"]),
        optimizer=synalinks.optimizers.RandomFewShot()
    )

    batch_size=32
    epochs=10

    history = await program.fit(
        x_train,
        y_train,
        validation_data=(x_test, y_test),
        batch_size=batch_size,
        epochs=epochs,
    )

    synalinks.utils.plot_history(history)

if __name__ == "__main__":
    asyncio.run(main())

training_history

Learn more

You can learn more by reading our documentation. If you have questions, the FAQ might help you.

Contributions

Contributions are welcome, either for the implementation of additional modules, metrics, or optimizers. For more information, or help for implementing your ideas (or ones from a paper), please join our discord.

Beware that every additional metric/module/optimizer should be approved by the core team, we want to keep the library minimal and clean as possible to avoid an uncontrolled growth leading to bad software practices like in most current leading LM frameworks.

Community

Discord Channel

Join our community to learn more about neuro-symbolic systems and the future of AI. We welcome the participation of people from very different backgrounds or education levels.

Credit

Synalinks would not be possible without the great work of the following open-source projects:

  • Keras for the graph-based computation backbone, API and overall code, design and philosophy.
  • DSPy for the modules/optimizers inspiration.
  • Pydantic for the backend data layer.
  • LiteLLM for the LMs integrations.

Releases

No releases published

Packages

No packages published