├── synalinks ├── src │ ├── hooks │ │ ├── __init__.py │ │ ├── hook_test.py │ │ └── hook.py │ ├── saving │ │ ├── __init__.py │ │ └── synalinks_saveable.py │ ├── datasets │ │ ├── __init__.py │ │ ├── gsm8k_test.py │ │ └── hotpotqa_test.py │ ├── modules │ │ ├── core │ │ │ ├── __init__.py │ │ │ ├── input_module_test.py │ │ │ ├── not_module.py │ │ │ ├── decision_test.py │ │ │ ├── identity.py │ │ │ └── branch_test.py │ │ ├── ttc │ │ │ └── __init__.py │ │ ├── agents │ │ │ └── __init__.py │ │ ├── knowledge │ │ │ └── __init__.py │ │ ├── merging │ │ │ ├── __init__.py │ │ │ ├── logical_and.py │ │ │ ├── logical_or.py │ │ │ ├── concat.py │ │ │ ├── logical_xor.py │ │ │ ├── logical_and_test.py │ │ │ ├── concat_test.py │ │ │ ├── logical_or_test.py │ │ │ └── logical_xor_test.py │ │ ├── synthesis │ │ │ └── __init__.py │ │ ├── retrievers │ │ │ ├── __init__.py │ │ │ ├── entity_retriever_test.py │ │ │ └── triplet_retriever_test.py │ │ ├── masking │ │ │ ├── __init__.py │ │ │ ├── in_mask_test.py │ │ │ ├── out_mask_test.py │ │ │ ├── out_mask.py │ │ │ └── in_mask.py │ │ └── __init__.py │ ├── trainers │ │ ├── __init__.py │ │ ├── data_adapters │ │ │ ├── generator_data_adapter_test.py │ │ │ ├── __init__.py │ │ │ ├── generator_data_adapter.py │ │ │ ├── array_data_adapter_test.py │ │ │ ├── data_adapter_utils.py │ │ │ └── data_adapter.py │ │ └── epoch_iterator_test.py │ ├── backend │ │ ├── pydantic │ │ │ ├── __init__.py │ │ │ └── module.py │ │ └── common │ │ │ ├── __init__.py │ │ │ ├── global_state_test.py │ │ │ ├── symbolic_scope.py │ │ │ ├── global_state.py │ │ │ ├── name_scope_test.py │ │ │ └── symbolic_data_model_test.py │ ├── utils │ │ ├── mcp │ │ │ ├── __init__.py │ │ │ └── test_common.py │ │ ├── __init__.py │ │ ├── plot_utils.py │ │ ├── module_utils.py │ │ ├── tool_utils_test.py │ │ └── naming.py │ ├── ops │ │ ├── operation_test.py │ │ ├── __init__.py │ │ └── symbolic_arguments.py │ ├── testing │ │ ├── __init__.py │ │ └── test_case.py │ ├── optimizers │ │ ├── __init__.py │ │ └── random_few_shot_test.py │ ├── knowledge_bases │ │ ├── __init__.py │ │ ├── database_adapters │ │ │ └── __init__.py │ │ └── knowledge_base_test.py │ ├── programs │ │ └── __init__.py │ ├── callbacks │ │ ├── __init__.py │ │ ├── history.py │ │ └── callback_test.py │ ├── version.py │ ├── tree │ │ └── __init__.py │ ├── embedding_models │ │ ├── embedding_model_test.py │ │ └── __init__.py │ ├── __init__.py │ ├── api_export.py │ ├── initializers │ │ ├── empty_initializer_test.py │ │ └── empty_initializer.py │ ├── language_models │ │ └── __init__.py │ └── rewards │ │ ├── lm_as_judge_test.py │ │ ├── cosine_similarity_test.py │ │ ├── exact_match_test.py │ │ └── exact_match.py └── api │ ├── ops │ ├── embedding_models │ │ └── __init__.py │ ├── knowledge_bases │ │ └── __init__.py │ ├── json │ │ └── __init__.py │ └── __init__.py │ ├── callbacks │ ├── monitor │ │ └── __init__.py │ └── __init__.py │ ├── optimizers │ └── __init__.py │ ├── datasets │ ├── __init__.py │ ├── gsm8k │ │ └── __init__.py │ ├── hotpotqa │ │ └── __init__.py │ └── arcagi │ │ └── __init__.py │ ├── programs │ └── __init__.py │ ├── hooks │ └── __init__.py │ ├── language_models │ └── __init__.py │ ├── embedding_models │ └── __init__.py │ ├── initializers │ └── __init__.py │ ├── tree │ └── __init__.py │ ├── rewards │ └── __init__.py │ ├── saving │ └── __init__.py │ ├── metrics │ └── __init__.py │ ├── config │ └── __init__.py │ └── modules │ └── __init__.py ├── docs ├── Synalinks API │ ├── Hooks API │ │ ├── Logger.md │ │ ├── Monitor.md │ │ ├── Base Hook class.md │ │ └── index.md │ ├── Ops API │ │ ├── JSON Ops.md │ │ ├── Embedding Models Ops.md │ │ ├── Language Models Ops.md │ │ └── index.md │ ├── Built-in Datasets │ │ ├── GSM8K.md │ │ ├── ARC-AGI.md │ │ ├── HotpotQA.md │ │ └── index.md │ ├── Callbacks API │ │ ├── Monitor.md │ │ ├── CSVLogger.md │ │ ├── Base Callback class.md │ │ ├── EarlyStopping.md │ │ ├── BackUpAndRestore.md │ │ ├── ProgramCheckpoint.md │ │ └── index.md │ ├── Optimizers API │ │ ├── OMEGA.md │ │ ├── RandomFewShot.md │ │ ├── Base Optimizer class.md │ │ └── index.md │ ├── Utilities │ │ ├── NLP utilities.md │ │ ├── Program plotting utilities.md │ │ └── More plotting utilities.md │ ├── Metrics │ │ ├── Base Metric class.md │ │ ├── FScore metrics.md │ │ ├── Regression metrics.md │ │ ├── Metric wrappers and reduction metrics.md │ │ └── index.md │ ├── Rewards │ │ ├── LMAsJudge reward.md │ │ ├── ExactMatch reward.md │ │ ├── Reward wrappers.md │ │ ├── CosineSimilarity reward.md │ │ └── index.md │ ├── Modules API │ │ ├── Base Module class.md │ │ ├── Core Modules │ │ │ ├── Action module.md │ │ │ ├── Branch module.md │ │ │ ├── Not module.md │ │ │ ├── Decision module.md │ │ │ ├── Generator module.md │ │ │ ├── Identity module.md │ │ │ ├── Input module.md │ │ │ └── index.md │ │ ├── Merging Modules │ │ │ ├── Concat module.md │ │ │ ├── Or module.md │ │ │ ├── And module.md │ │ │ ├── Xor module.md │ │ │ └── index.md │ │ ├── Masking Modules │ │ │ ├── InMask module.md │ │ │ ├── OutMask module.md │ │ │ └── index.md │ │ ├── Knowledge Modules │ │ │ ├── Embedding module.md │ │ │ ├── UpdateKnowledge module.md │ │ │ └── index.md │ │ ├── Test Time Compute Modules │ │ │ ├── SelfCritique module.md │ │ │ ├── ChainOfThought module.md │ │ │ └── index.md │ │ ├── Retrievers Modules │ │ │ ├── EntityRetriever module.md │ │ │ ├── TripletRetriever module.md │ │ │ └── index.md │ │ ├── Synthesis Modules │ │ │ ├── PythonSynthesis module.md │ │ │ ├── SequentialPlanSynthesis module.md │ │ │ └── index.md │ │ └── Agents Modules │ │ │ ├── FunctionCallingAgent module.md │ │ │ └── index.md │ ├── Data Models API │ │ ├── The Base DataModels.md │ │ ├── The DataModel class.md │ │ ├── The Variable class.md │ │ ├── The JsonDataModel class.md │ │ ├── The SymbolicDataModel class.md │ │ └── index.md │ ├── Programs API │ │ ├── Program training API.md │ │ ├── The Program class.md │ │ ├── The Sequential class.md │ │ ├── Program Saving API │ │ │ ├── Program saving and loading.md │ │ │ └── Variable saving and loading.md │ │ └── index.md │ ├── Config.md │ ├── Knowledge Bases API.md │ ├── Language Models API.md │ └── Embedding Models API.md ├── assets │ ├── g_v_0.png │ ├── g_v_1.png │ ├── g_v_2.png │ ├── g_v_3.png │ ├── g_v_4.png │ ├── 025d127b.png │ ├── logical_or.png │ ├── simple_kag.png │ ├── simple_rag.png │ ├── concatenation.png │ ├── logical_and.png │ ├── decision_making.png │ ├── gsm8k_baseline.png │ ├── one_stage_graph.png │ ├── simple_chatbot.png │ ├── synalinks_logo.png │ ├── two_stage_graph.png │ ├── chain_of_thought.png │ ├── evaluation_metrics.png │ ├── multi_stage_graph.png │ ├── parallel_branches.png │ ├── simple_rag_program.png │ ├── synalinks_favicon.png │ ├── training_history.png │ ├── conditional_branches.png │ ├── one_stage_extraction.png │ ├── two_stage_extraction.png │ ├── backtracking_of_thought.png │ ├── multi_stage_extraction.png │ ├── gsm8k_evaluation_comparison.png │ ├── evaluation_metrics_comparison.png │ ├── relations_only_one_stage_graph.png │ ├── relations_only_multi_stage_graph.png │ ├── training_history_with_mean_and_std.png │ ├── evaluation_metrics_with_mean_and_std.png │ ├── relations_only_multi_stage_extraction.png │ └── evaluation_comparaison_with_mean_and_std.png ├── stylesheets │ └── extra.css ├── Monitoring │ └── Arize Phoenix.md └── Graph Visualization │ └── Using G.V() as Graph IDE.md ├── shell ├── purge_docker.sh ├── stop_docker.sh ├── upgrade.sh ├── test.sh ├── install.sh ├── uv.sh ├── doc.sh ├── publish.sh ├── format.sh ├── lint.sh └── api_gen.sh ├── img ├── synalinks.png └── synalinks_square.png ├── examples ├── first_programs │ ├── chain_of_thought.png │ ├── chain_of_thought_functional.png │ └── chain_of_thought_sequential.png ├── knowledge │ ├── retrieval │ │ ├── simple_kag.png │ │ ├── simple_rag.png │ │ ├── retrieval_augmented_generation.py │ │ ├── knowledge_graph_schema.py │ │ └── knowledge_augmented_generation.py │ └── extraction │ │ ├── one_stage_extraction.png │ │ ├── two_stage_extraction.png │ │ ├── multi_stage_extraction.png │ │ ├── relations_only_one_stage_extraction.png │ │ ├── relations_only_multi_stage_extraction.png │ │ ├── knowledge_dataset.py │ │ ├── knowledge_graph_schema.py │ │ ├── relations_only_one_stage_kg_extraction.py │ │ └── one_stage_kg_extraction.py ├── training_programs │ ├── gsm8k_baseline.png │ ├── gsm8k_evaluation_comparison.png │ ├── gsm8k_baseline_training_history.png │ └── evaluation_metrics_with_mean_and_std.png ├── implementing_custom_modules_and_programs_via_subclassing │ └── backtracking_of_thought.png ├── datasets │ └── knowledge_graph │ │ ├── european_travel_guide.txt │ │ ├── olympic_history.txt │ │ ├── world_war_2.txt │ │ ├── cultural_heritage_sites.txt │ │ └── modern_political_events.txt ├── conversational_application │ └── conversational_application.py ├── json_operators │ ├── logical_and.py │ ├── concatenate.py │ └── concatenate_part_2.py └── agents │ ├── mcp_tool_agent │ ├── mcp_server.py │ └── mcp_agent.py │ └── autonomous_function_calling_agent.py ├── .github └── workflows │ ├── docs.yml │ └── tests.yml ├── coverage-badge.svg └── .gitignore /synalinks/src/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/saving/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/modules/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/modules/ttc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/trainers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/backend/pydantic/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/hooks/hook_test.py: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /synalinks/src/modules/agents/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/modules/knowledge/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/modules/synthesis/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/utils/mcp/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /synalinks/src/modules/retrievers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /synalinks/src/ops/operation_test.py: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /docs/Synalinks API/Hooks API/Logger.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.hooks.logger -------------------------------------------------------------------------------- /docs/Synalinks API/Ops API/JSON Ops.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.ops.json -------------------------------------------------------------------------------- /docs/Synalinks API/Hooks API/Monitor.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.hooks.monitor -------------------------------------------------------------------------------- /synalinks/src/trainers/data_adapters/generator_data_adapter_test.py: -------------------------------------------------------------------------------- 1 | # TODO 2 | -------------------------------------------------------------------------------- /docs/Synalinks API/Built-in Datasets/GSM8K.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.datasets.gsm8k -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/Monitor.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.monitor -------------------------------------------------------------------------------- /docs/Synalinks API/Hooks API/Base Hook class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.hooks.hook -------------------------------------------------------------------------------- /docs/Synalinks API/Optimizers API/OMEGA.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.optimizers.omega -------------------------------------------------------------------------------- /docs/Synalinks API/Utilities/NLP utilities.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.utils.nlp_utils -------------------------------------------------------------------------------- /docs/Synalinks API/Built-in Datasets/ARC-AGI.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.datasets.arcagi -------------------------------------------------------------------------------- /docs/Synalinks API/Built-in Datasets/HotpotQA.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.datasets.hotpotqa -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/CSVLogger.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.csv_logger -------------------------------------------------------------------------------- /docs/Synalinks API/Metrics/Base Metric class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.metrics.metric -------------------------------------------------------------------------------- /docs/Synalinks API/Rewards/LMAsJudge reward.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.rewards.lm_as_judge -------------------------------------------------------------------------------- /shell/purge_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Euo pipefail 3 | 4 | docker system prune -a -------------------------------------------------------------------------------- /docs/Synalinks API/Metrics/FScore metrics.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.metrics.f_score_metrics -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Base Module class.md: -------------------------------------------------------------------------------- 1 | ::: synalinks.src.modules.module.Module -------------------------------------------------------------------------------- /docs/Synalinks API/Ops API/Embedding Models Ops.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.ops.embedding_models -------------------------------------------------------------------------------- /docs/Synalinks API/Ops API/Language Models Ops.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.ops.language_models -------------------------------------------------------------------------------- /docs/Synalinks API/Rewards/ExactMatch reward.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.rewards.exact_match -------------------------------------------------------------------------------- /docs/Synalinks API/Rewards/Reward wrappers.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.rewards.reward_wrappers -------------------------------------------------------------------------------- /img/synalinks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/img/synalinks.png -------------------------------------------------------------------------------- /synalinks/src/testing/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.testing.test_case import TestCase 2 | -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/Base Callback class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.callback -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/EarlyStopping.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.early_stopping -------------------------------------------------------------------------------- /docs/Synalinks API/Metrics/Regression metrics.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.metrics.regression_metrics -------------------------------------------------------------------------------- /docs/assets/g_v_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/g_v_0.png -------------------------------------------------------------------------------- /docs/assets/g_v_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/g_v_1.png -------------------------------------------------------------------------------- /docs/assets/g_v_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/g_v_2.png -------------------------------------------------------------------------------- /docs/assets/g_v_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/g_v_3.png -------------------------------------------------------------------------------- /docs/assets/g_v_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/g_v_4.png -------------------------------------------------------------------------------- /synalinks/src/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.optimizers.optimizer import Optimizer 2 | -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/BackUpAndRestore.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.backup_and_restore -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/The Base DataModels.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.pydantic.base -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/The DataModel class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.pydantic.core -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Action module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.action -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Branch module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.branch -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Not module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.not_module -------------------------------------------------------------------------------- /docs/Synalinks API/Optimizers API/RandomFewShot.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.optimizers.random_few_shot -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/Program training API.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.trainers.trainer.Trainer -------------------------------------------------------------------------------- /docs/Synalinks API/Rewards/CosineSimilarity reward.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.rewards.cosine_similarity -------------------------------------------------------------------------------- /docs/assets/025d127b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/025d127b.png -------------------------------------------------------------------------------- /img/synalinks_square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/img/synalinks_square.png -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/ProgramCheckpoint.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.callbacks.program_checkpoint -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Decision module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.decision -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Generator module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.generator -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Identity module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.identity -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/Input module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.core.input_module -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Merging Modules/Concat module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.merging.concat -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Merging Modules/Or module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.merging.logical_or -------------------------------------------------------------------------------- /docs/Synalinks API/Utilities/Program plotting utilities.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.utils.program_visualization -------------------------------------------------------------------------------- /docs/assets/logical_or.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/logical_or.png -------------------------------------------------------------------------------- /docs/assets/simple_kag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/simple_kag.png -------------------------------------------------------------------------------- /docs/assets/simple_rag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/simple_rag.png -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/The Variable class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.common.variables.Variable -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Masking Modules/InMask module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.masking.in_mask -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Masking Modules/OutMask module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.masking.out_mask -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Merging Modules/And module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.merging.logical_and -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Merging Modules/Xor module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.merging.logical_xor -------------------------------------------------------------------------------- /docs/Synalinks API/Optimizers API/Base Optimizer class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.optimizers.optimizer.Optimizer -------------------------------------------------------------------------------- /docs/assets/concatenation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/concatenation.png -------------------------------------------------------------------------------- /docs/assets/logical_and.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/logical_and.png -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/The JsonDataModel class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.common.json_data_model -------------------------------------------------------------------------------- /docs/Synalinks API/Metrics/Metric wrappers and reduction metrics.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.metrics.reduction_metrics -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Knowledge Modules/Embedding module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.knowledge.embedding -------------------------------------------------------------------------------- /docs/assets/decision_making.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/decision_making.png -------------------------------------------------------------------------------- /docs/assets/gsm8k_baseline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/gsm8k_baseline.png -------------------------------------------------------------------------------- /docs/assets/one_stage_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/one_stage_graph.png -------------------------------------------------------------------------------- /docs/assets/simple_chatbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/simple_chatbot.png -------------------------------------------------------------------------------- /docs/assets/synalinks_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/synalinks_logo.png -------------------------------------------------------------------------------- /docs/assets/two_stage_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/two_stage_graph.png -------------------------------------------------------------------------------- /synalinks/src/knowledge_bases/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.knowledge_bases.knowledge_base import KnowledgeBase 2 | -------------------------------------------------------------------------------- /docs/Synalinks API/Config.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.common.global_state 3 | 4 | ::: synalinks.src.backend.config -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/The SymbolicDataModel class.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.backend.common.symbolic_data_model -------------------------------------------------------------------------------- /docs/Synalinks API/Knowledge Bases API.md: -------------------------------------------------------------------------------- 1 | # Knowledge Bases API 2 | 3 | ::: synalinks.src.knowledge_bases.knowledge_base -------------------------------------------------------------------------------- /docs/Synalinks API/Language Models API.md: -------------------------------------------------------------------------------- 1 | # Language Models API 2 | 3 | ::: synalinks.src.language_models.language_model -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/The Program class.md: -------------------------------------------------------------------------------- 1 | # The Program class 2 | 3 | ::: synalinks.src.programs.program.Program -------------------------------------------------------------------------------- /docs/assets/chain_of_thought.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/chain_of_thought.png -------------------------------------------------------------------------------- /docs/assets/evaluation_metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/evaluation_metrics.png -------------------------------------------------------------------------------- /docs/assets/multi_stage_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/multi_stage_graph.png -------------------------------------------------------------------------------- /docs/assets/parallel_branches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/parallel_branches.png -------------------------------------------------------------------------------- /docs/assets/simple_rag_program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/simple_rag_program.png -------------------------------------------------------------------------------- /docs/assets/synalinks_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/synalinks_favicon.png -------------------------------------------------------------------------------- /docs/assets/training_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/training_history.png -------------------------------------------------------------------------------- /docs/Synalinks API/Embedding Models API.md: -------------------------------------------------------------------------------- 1 | # Embedding Models API 2 | 3 | ::: synalinks.src.embedding_models.embedding_model -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Test Time Compute Modules/SelfCritique module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.ttc.self_critique -------------------------------------------------------------------------------- /docs/assets/conditional_branches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/conditional_branches.png -------------------------------------------------------------------------------- /docs/assets/one_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/one_stage_extraction.png -------------------------------------------------------------------------------- /docs/assets/two_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/two_stage_extraction.png -------------------------------------------------------------------------------- /shell/stop_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Euo pipefail 3 | 4 | docker stop $(docker ps -a -q) 5 | docker rm $(docker ps -a -q) -------------------------------------------------------------------------------- /shell/upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | # Upgrade packages 5 | uv lock --upgrade 6 | # Sync lock 7 | uv sync -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Knowledge Modules/UpdateKnowledge module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.knowledge.update_knowledge -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Retrievers Modules/EntityRetriever module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.retrievers.entity_retriever -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Synthesis Modules/PythonSynthesis module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.synthesis.python_synthesis -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Test Time Compute Modules/ChainOfThought module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.ttc.chain_of_thought -------------------------------------------------------------------------------- /docs/assets/backtracking_of_thought.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/backtracking_of_thought.png -------------------------------------------------------------------------------- /docs/assets/multi_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/multi_stage_extraction.png -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Agents Modules/FunctionCallingAgent module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.agents.function_calling_agent -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Retrievers Modules/TripletRetriever module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.retrievers.triplet_retriever -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/The Sequential class.md: -------------------------------------------------------------------------------- 1 | # The Sequential class 2 | 3 | ::: synalinks.src.programs.sequential.Sequential -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Agents Modules/index.md: -------------------------------------------------------------------------------- 1 | # Agent Modules 2 | 3 | - [FunctionCallingAgent module](FunctionCallingAgent module.md) -------------------------------------------------------------------------------- /docs/Synalinks API/Utilities/More plotting utilities.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.utils.plot_history 3 | 4 | ::: synalinks.src.utils.plot_metrics -------------------------------------------------------------------------------- /docs/assets/gsm8k_evaluation_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/gsm8k_evaluation_comparison.png -------------------------------------------------------------------------------- /examples/first_programs/chain_of_thought.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/first_programs/chain_of_thought.png -------------------------------------------------------------------------------- /examples/knowledge/retrieval/simple_kag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/retrieval/simple_kag.png -------------------------------------------------------------------------------- /examples/knowledge/retrieval/simple_rag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/retrieval/simple_rag.png -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Synthesis Modules/SequentialPlanSynthesis module.md: -------------------------------------------------------------------------------- 1 | 2 | ::: synalinks.src.modules.synthesis.sequential_plan_synthesis -------------------------------------------------------------------------------- /docs/assets/evaluation_metrics_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/evaluation_metrics_comparison.png -------------------------------------------------------------------------------- /docs/assets/relations_only_one_stage_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/relations_only_one_stage_graph.png -------------------------------------------------------------------------------- /examples/training_programs/gsm8k_baseline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/training_programs/gsm8k_baseline.png -------------------------------------------------------------------------------- /docs/assets/relations_only_multi_stage_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/relations_only_multi_stage_graph.png -------------------------------------------------------------------------------- /docs/assets/training_history_with_mean_and_std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/training_history_with_mean_and_std.png -------------------------------------------------------------------------------- /synalinks/src/backend/pydantic/module.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | 4 | class PydanticModule: 5 | pass 6 | -------------------------------------------------------------------------------- /docs/assets/evaluation_metrics_with_mean_and_std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/evaluation_metrics_with_mean_and_std.png -------------------------------------------------------------------------------- /docs/assets/relations_only_multi_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/relations_only_multi_stage_extraction.png -------------------------------------------------------------------------------- /examples/knowledge/extraction/one_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/extraction/one_stage_extraction.png -------------------------------------------------------------------------------- /examples/knowledge/extraction/two_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/extraction/two_stage_extraction.png -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Masking Modules/index.md: -------------------------------------------------------------------------------- 1 | # Masking Modules 2 | 3 | - [InMask module](InMask module.md) 4 | - [OutMask module](OutMask module.md) 5 | -------------------------------------------------------------------------------- /docs/assets/evaluation_comparaison_with_mean_and_std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/docs/assets/evaluation_comparaison_with_mean_and_std.png -------------------------------------------------------------------------------- /examples/first_programs/chain_of_thought_functional.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/first_programs/chain_of_thought_functional.png -------------------------------------------------------------------------------- /examples/first_programs/chain_of_thought_sequential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/first_programs/chain_of_thought_sequential.png -------------------------------------------------------------------------------- /examples/knowledge/extraction/multi_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/extraction/multi_stage_extraction.png -------------------------------------------------------------------------------- /shell/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | uv run pytest --cov-config=pyproject.toml 5 | uvx --from 'genbadge[coverage]' genbadge coverage -i coverage.xml -------------------------------------------------------------------------------- /synalinks/src/modules/masking/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.modules.masking.in_mask import InMask 2 | from synalinks.src.modules.masking.out_mask import OutMask 3 | -------------------------------------------------------------------------------- /examples/training_programs/gsm8k_evaluation_comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/training_programs/gsm8k_evaluation_comparison.png -------------------------------------------------------------------------------- /examples/training_programs/gsm8k_baseline_training_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/training_programs/gsm8k_baseline_training_history.png -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Knowledge Modules/index.md: -------------------------------------------------------------------------------- 1 | # Knowledge Modules 2 | 3 | - [Embedding module](Embedding module.md) 4 | - [UpdateKnowledge module](UpdateKnowledge module.md) -------------------------------------------------------------------------------- /examples/knowledge/extraction/relations_only_one_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/extraction/relations_only_one_stage_extraction.png -------------------------------------------------------------------------------- /examples/training_programs/evaluation_metrics_with_mean_and_std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/training_programs/evaluation_metrics_with_mean_and_std.png -------------------------------------------------------------------------------- /examples/knowledge/extraction/relations_only_multi_stage_extraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/knowledge/extraction/relations_only_multi_stage_extraction.png -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Retrievers Modules/index.md: -------------------------------------------------------------------------------- 1 | # Retrievers Modules 2 | 3 | - [EntityRetriever module](EntityRetriever module.md) 4 | - [TripletRetriever module](TripletRetriever module.md) -------------------------------------------------------------------------------- /shell/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | # Cleanup the cache 5 | uv cache clean 6 | # Uninstall current version 7 | uv pip uninstall . 8 | # Install new version 9 | uv pip install . -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Test Time Compute Modules/index.md: -------------------------------------------------------------------------------- 1 | # Test Time Compute Modules 2 | 3 | - [ChainOfThought module](ChainOfThought module.md) 4 | - [SelfCritique module](SelfCritique module.md) -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Synthesis Modules/index.md: -------------------------------------------------------------------------------- 1 | # Synthesis Modules 2 | 3 | - [PythonSynthesis module](PythonSynthesis module.md) 4 | - [SequentialPlanSynthesis module](SequentialPlanSynthesis module.md) -------------------------------------------------------------------------------- /synalinks/src/programs/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.programs.functional import Functional 2 | from synalinks.src.programs.program import Program 3 | from synalinks.src.programs.sequential import Sequential 4 | -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Merging Modules/index.md: -------------------------------------------------------------------------------- 1 | # Merging Modules 2 | 3 | - [Concat module](Concat module.md) 4 | - [And module](And module.md) 5 | - [Or module](Or module.md) 6 | - [Xor module](Xor module.md) -------------------------------------------------------------------------------- /shell/uv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | base_dir=$(dirname $(dirname $0)) 5 | 6 | echo "Installing UV project manager..." 7 | 8 | curl -LsSf https://astral.sh/uv/install.sh | sh 9 | 10 | uv venv -------------------------------------------------------------------------------- /synalinks/src/modules/retrievers/entity_retriever_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | 5 | 6 | class EntityRetrieverTest(testing.TestCase): 7 | pass 8 | -------------------------------------------------------------------------------- /examples/implementing_custom_modules_and_programs_via_subclassing/backtracking_of_thought.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SynaLinks/synalinks/HEAD/examples/implementing_custom_modules_and_programs_via_subclassing/backtracking_of_thought.png -------------------------------------------------------------------------------- /synalinks/src/modules/retrievers/triplet_retriever_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | 5 | 6 | class KnowledgeRetrieverTest(testing.TestCase): 7 | pass 8 | -------------------------------------------------------------------------------- /shell/doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | uv pip install mkdocs 5 | uv pip install mkdocs-material 6 | uv pip install mkdocstrings[python] 7 | uv pip install mkdocs-glightbox 8 | uv pip install mkdocs-llmstxt 9 | 10 | uv run mkdocs serve -------------------------------------------------------------------------------- /shell/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | # Remove dist folder 5 | # To avoid problems with uv publish 6 | # that don't find the last version 7 | rm -rf dist/ 8 | # Build the project 9 | uv build 10 | # Publish on Pypi 11 | uv publish -------------------------------------------------------------------------------- /synalinks/api/ops/embedding_models/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.ops.embedding_models import embedding as embedding 8 | -------------------------------------------------------------------------------- /synalinks/src/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.utils.python_utils import default 2 | from synalinks.src.utils.python_utils import is_default 3 | from synalinks.src.utils.python_utils import removeprefix 4 | from synalinks.src.utils.python_utils import removesuffix 5 | -------------------------------------------------------------------------------- /synalinks/src/callbacks/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.callbacks.callback import Callback 2 | from synalinks.src.callbacks.callback_list import CallbackList 3 | from synalinks.src.callbacks.history import History 4 | from synalinks.src.callbacks.progbar_logger import ProgbarLogger 5 | -------------------------------------------------------------------------------- /docs/Synalinks API/Hooks API/index.md: -------------------------------------------------------------------------------- 1 | # Hook API 2 | 3 | A hook is an object that can perform various actions at the begining/end of a module's call. 4 | 5 | ## Hooks Overview 6 | 7 | - [Base Hook class](Base Hook class.md) 8 | - [Logger hook](Logger.md) 9 | - [Monitor hook](Monitor.md) -------------------------------------------------------------------------------- /shell/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Euo pipefail 3 | 4 | base_dir=$(dirname $(dirname $0)) 5 | 6 | uvx black --line-length 90 synalinks/src 7 | 8 | uvx ruff check --config "${base_dir}/pyproject.toml" --fix . 9 | 10 | uvx ruff format --config "${base_dir}/pyproject.toml" . 11 | -------------------------------------------------------------------------------- /shell/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Euo pipefail 3 | 4 | base_dir=$(dirname $(dirname $0)) 5 | 6 | uvx ruff check --config "${base_dir}/pyproject.toml" . 7 | exitcode=$? 8 | 9 | uvx ruff format --check --config "${base_dir}/pyproject.toml" . 10 | exitcode=$(($exitcode + $?)) 11 | 12 | exit $exitcode 13 | -------------------------------------------------------------------------------- /synalinks/api/callbacks/monitor/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.callbacks.monitor import LogEntry as LogEntry 8 | from synalinks.src.hooks.monitor import Span as Span 9 | -------------------------------------------------------------------------------- /synalinks/api/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.optimizers.omega import OMEGA as OMEGA 8 | from synalinks.src.optimizers.random_few_shot import RandomFewShot as RandomFewShot 9 | -------------------------------------------------------------------------------- /shell/api_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -Eeuo pipefail 3 | 4 | base_dir=$(dirname $(dirname $0)) 5 | 6 | echo "Generating api directory with public APIs..." 7 | # Generate API Files 8 | uv run "${base_dir}"/api_gen.py 9 | 10 | echo "Formatting api directory..." 11 | # Format API Files 12 | bash "${base_dir}"/shell/format.sh 13 | -------------------------------------------------------------------------------- /synalinks/src/version.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src.api_export import synalinks_export 4 | 5 | # Unique source of truth for the version number. 6 | __version__ = "0.5.322" 7 | 8 | 9 | @synalinks_export("synalinks.version") 10 | def version(): 11 | return __version__ 12 | -------------------------------------------------------------------------------- /docs/Synalinks API/Modules API/Core Modules/index.md: -------------------------------------------------------------------------------- 1 | # Core Modules 2 | 3 | - [Input module](Input module.md) 4 | - [Identity module](Identity module.md) 5 | - [Not module](Not module.md) 6 | - [Generator module](Generator module.md) 7 | - [Decision module](Decision module.md) 8 | - [Action module](Action module.md) 9 | - [Branch module](Branch module.md) -------------------------------------------------------------------------------- /synalinks/api/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.api.datasets import arcagi as arcagi 8 | from synalinks.api.datasets import gsm8k as gsm8k 9 | from synalinks.api.datasets import hotpotqa as hotpotqa 10 | -------------------------------------------------------------------------------- /synalinks/api/ops/knowledge_bases/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.ops.knowledge_bases import similarity_search as similarity_search 8 | from synalinks.src.ops.knowledge_bases import update_knowledge as update_knowledge 9 | -------------------------------------------------------------------------------- /synalinks/src/backend/common/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.backend.common.json_schema_utils import standardize_schema 2 | from synalinks.src.backend.common.name_scope import name_scope 3 | from synalinks.src.backend.common.symbolic_data_model import SymbolicDataModel 4 | from synalinks.src.backend.common.variables import Variable 5 | from synalinks.src.backend.pydantic import core 6 | -------------------------------------------------------------------------------- /synalinks/api/programs/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.programs.program import Program as Program 8 | from synalinks.src.programs.program import program_from_json as program_from_json 9 | from synalinks.src.programs.sequential import Sequential as Sequential 10 | -------------------------------------------------------------------------------- /synalinks/api/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.hooks.hook import Hook as Hook 8 | from synalinks.src.hooks.hook_list import HookList as HookList 9 | from synalinks.src.hooks.logger import Logger as Logger 10 | from synalinks.src.hooks.monitor import Monitor as Monitor 11 | -------------------------------------------------------------------------------- /synalinks/api/language_models/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.language_models import deserialize as deserialize 8 | from synalinks.src.language_models import serialize as serialize 9 | from synalinks.src.language_models.language_model import LanguageModel as LanguageModel 10 | -------------------------------------------------------------------------------- /synalinks/api/datasets/gsm8k/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.datasets.gsm8k import get_input_data_model as get_input_data_model 8 | from synalinks.src.datasets.gsm8k import get_output_data_model as get_output_data_model 9 | from synalinks.src.datasets.gsm8k import load_data as load_data 10 | -------------------------------------------------------------------------------- /synalinks/src/saving/synalinks_saveable.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/saving/keras_saveable.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | 6 | class SynalinksSaveable: 7 | def _obj_type(self): 8 | raise NotImplementedError( 9 | "SynalinksSaveable subclases must provide an implementation for `obj_type()`" 10 | ) 11 | -------------------------------------------------------------------------------- /synalinks/api/embedding_models/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.embedding_models import deserialize as deserialize 8 | from synalinks.src.embedding_models import serialize as serialize 9 | from synalinks.src.embedding_models.embedding_model import ( 10 | EmbeddingModel as EmbeddingModel, 11 | ) 12 | -------------------------------------------------------------------------------- /synalinks/src/datasets/gsm8k_test.py: -------------------------------------------------------------------------------- 1 | # from synalinks.src import testing 2 | # from synalinks.src.datasets.gsm8k import load_data 3 | 4 | 5 | # class GSM8kTest(testing.TestCase): 6 | # def test_load_data(self): 7 | # (x_train, y_train), (x_test, y_test) = load_data() 8 | # self.assertTrue(len(x_train) > 0) 9 | # self.assertTrue(len(y_train) > 0) 10 | # self.assertTrue(len(x_test) > 0) 11 | # self.assertTrue(len(y_test) > 0) 12 | -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/Program Saving API/Program saving and loading.md: -------------------------------------------------------------------------------- 1 | ### Saving programs into a JSON file 2 | 3 | ::: synalinks.src.programs.program.Program.save 4 | 5 | ### Saving programs into a JSON string 6 | 7 | ::: synalinks.src.programs.program.Program.to_json 8 | 9 | ## Loading programs from a JSON file 10 | 11 | ::: synalinks.src.programs.program.Program.load 12 | 13 | ### Loading a program from a JSON string 14 | 15 | ::: synalinks.src.programs.program.program_from_json -------------------------------------------------------------------------------- /examples/datasets/knowledge_graph/european_travel_guide.txt: -------------------------------------------------------------------------------- 1 | Paris is the capital of France and is located in western Europe. 2 | The city is home to the famous Louvre Museum, which is located in the heart of Paris. 3 | London is the capital of the United Kingdom and is located across the English Channel from France. 4 | The Napoleonic Wars took place across Europe in the early 19th century, with major battles occurring in both France and the United Kingdom. 5 | Rome is the capital of Italy and is located in southern Europe. -------------------------------------------------------------------------------- /examples/datasets/knowledge_graph/olympic_history.txt: -------------------------------------------------------------------------------- 1 | The 2012 Summer Olympics took place in London, bringing together athletes from around the world. 2 | London is the capital of the United Kingdom and hosted numerous events throughout the city. 3 | The Olympic Games also took place in Paris in 1900 and 1924. 4 | Paris is the capital of France and has a rich sporting heritage. 5 | Rome is the capital of Italy and hosted the Summer Olympics in 1960. 6 | The Olympic flame ceremony took place in the Colosseum, which is located in Rome. -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/Program Saving API/Variable saving and loading.md: -------------------------------------------------------------------------------- 1 | 2 | ## Saving variables into a JSON file 3 | 4 | ::: synalinks.src.programs.program.Program.save_variables 5 | 6 | ## Saving variables into JSON dict 7 | 8 | ::: synalinks.src.programs.program.Program.get_state_tree 9 | 10 | ## Loading variables from a JSON file 11 | 12 | ::: synalinks.src.programs.program.Program.load_variables 13 | 14 | ## Load variables from a JSON dict 15 | 16 | ::: synalinks.src.programs.program.Program.set_state_tree -------------------------------------------------------------------------------- /examples/datasets/knowledge_graph/world_war_2.txt: -------------------------------------------------------------------------------- 1 | The Liberation of Paris took place in August 1944, marking a turning point in World War II. 2 | Paris is the capital of France and had been under German occupation. 3 | London is the capital of the United Kingdom and endured the Blitz during the war. 4 | The London Blitz took place from 1940 to 1941, with bombing raids targeting the city. 5 | Rome is the capital of Italy and was liberated by Allied forces in June 1944. 6 | The Battle of Monte Cassino took place near Rome in early 1944. -------------------------------------------------------------------------------- /synalinks/src/tree/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.tree.tree_api import assert_same_paths 2 | from synalinks.src.tree.tree_api import assert_same_structure 3 | from synalinks.src.tree.tree_api import flatten 4 | from synalinks.src.tree.tree_api import is_nested 5 | from synalinks.src.tree.tree_api import lists_to_tuples 6 | from synalinks.src.tree.tree_api import map_structure 7 | from synalinks.src.tree.tree_api import pack_sequence_as 8 | from synalinks.src.tree.tree_api import register_tree_node_class 9 | from synalinks.src.tree.tree_api import traverse 10 | -------------------------------------------------------------------------------- /examples/datasets/knowledge_graph/cultural_heritage_sites.txt: -------------------------------------------------------------------------------- 1 | The Eiffel Tower is located in Paris, which is the capital of France. 2 | This iconic landmark attracts millions of visitors annually. 3 | Big Ben is located in London, the capital of the United Kingdom. 4 | The tower is part of the Palace of Westminster complex. 5 | The Colosseum is located in Rome, which is the capital of Italy. 6 | Ancient gladiatorial games took place in the Colosseum during the Roman Empire. 7 | The Renaissance movement began in Italy, with Rome playing a central role in this cultural transformation. 8 | -------------------------------------------------------------------------------- /docs/Synalinks API/Metrics/index.md: -------------------------------------------------------------------------------- 1 | # Metrics 2 | 3 | A `Metric` is a function that is used to judge the performance of your program. 4 | 5 | Metric functions are similar to reward functions, except that the results from evaluating a metric are not used when training the program. Note that you may use any reward function as a metric. 6 | 7 | ## Metrics Overview 8 | 9 | - [Base Metric class](Base Metric class.md) 10 | - [Metric wrappers and reduction metrics](Metric wrappers and reduction metrics.md) 11 | - [Regression metrics](Regression metrics.md) 12 | - [FScore metrics](FScore metrics.md) -------------------------------------------------------------------------------- /synalinks/api/initializers/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.initializers import deserialize as deserialize 8 | from synalinks.src.initializers import get as get 9 | from synalinks.src.initializers import serialize as serialize 10 | from synalinks.src.initializers.empty_initializer import Empty as Empty 11 | from synalinks.src.initializers.empty_initializer import Empty as empty 12 | from synalinks.src.initializers.initializer import Initializer as Initializer 13 | -------------------------------------------------------------------------------- /synalinks/api/datasets/hotpotqa/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.datasets.hotpotqa import get_input_data_model as get_input_data_model 8 | from synalinks.src.datasets.hotpotqa import ( 9 | get_knowledge_data_model as get_knowledge_data_model, 10 | ) 11 | from synalinks.src.datasets.hotpotqa import get_output_data_model as get_output_data_model 12 | from synalinks.src.datasets.hotpotqa import load_data as load_data 13 | from synalinks.src.datasets.hotpotqa import load_knowledge as load_knowledge 14 | -------------------------------------------------------------------------------- /docs/Synalinks API/Ops API/index.md: -------------------------------------------------------------------------------- 1 | # Ops API 2 | 3 | --- 4 | 5 | ## JSON Ops 6 | 7 | - [concat function](JSON Ops.md) 8 | - [factorize function](JSON Ops.md) 9 | - [in_mask function](JSON Ops.md) 10 | - [out_mask function](JSON Ops.md) 11 | - [logical_and function](JSON Ops.md) 12 | - [logical_or function](JSON Ops.md) 13 | - [logical_xor function](JSON Ops.md) 14 | - [suffix function](JSON Ops.md) 15 | - [prefix function](JSON Ops.md) 16 | 17 | --- 18 | 19 | ## Language Models Ops 20 | 21 | - [predict function](Language Models Ops.md) 22 | 23 | --- 24 | 25 | ## Embedding Models Ops 26 | 27 | - [embedding function](Embedding Models Ops.md) 28 | 29 | --- -------------------------------------------------------------------------------- /examples/datasets/knowledge_graph/modern_political_events.txt: -------------------------------------------------------------------------------- 1 | The European Union summit took place in Paris in 2023, with leaders gathering to discuss climate policy. 2 | Paris is the capital of France and frequently hosts international diplomatic meetings. 3 | Brexit negotiations took place in London over several years, fundamentally changing the relationship between the United Kingdom and Europe. 4 | London is the capital of the United Kingdom and served as the center for these historic discussions. 5 | The Vatican Summit took place in Rome, addressing global humanitarian issues. 6 | Rome is the capital of Italy and often serves as a venue for international religious gatherings. -------------------------------------------------------------------------------- /synalinks/src/datasets/hotpotqa_test.py: -------------------------------------------------------------------------------- 1 | # # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | # from synalinks.src import testing 4 | # from synalinks.src.datasets.hotpotqa import load_data 5 | # from synalinks.src.datasets.hotpotqa import load_knowledge 6 | 7 | 8 | # class HotPotQATest(testing.TestCase): 9 | # def test_load_knowledge(self): 10 | # knowledge = load_knowledge() 11 | # self.assertTrue(len(knowledge) > 0) 12 | 13 | # def test_load_data(self): 14 | # (x_train, y_train), (x_test, y_test) = load_data() 15 | # self.assertTrue(len(x_train) > 0) 16 | # self.assertTrue(len(y_train) > 0) 17 | # self.assertTrue(len(x_test) > 0) 18 | # self.assertTrue(len(y_test) > 0) 19 | -------------------------------------------------------------------------------- /synalinks/src/knowledge_bases/database_adapters/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.knowledge_bases.database_adapters.database_adapter import ( 2 | DatabaseAdapter, 3 | ) 4 | from synalinks.src.knowledge_bases.database_adapters.memgraph_adapter import ( 5 | MemGraphAdapter, 6 | ) 7 | from synalinks.src.knowledge_bases.database_adapters.neo4j_adapter import Neo4JAdapter 8 | 9 | # from synalinks.src.knowledge_bases.database_adapters.kuzu_adapter import KuzuAdapter 10 | 11 | 12 | def get(uri): 13 | if uri.startswith("neo4j"): 14 | return Neo4JAdapter 15 | elif uri.startswith("memgraph"): 16 | return MemGraphAdapter 17 | # elif uri.startswith("kuzu"): 18 | # return KuzuAdapter 19 | else: 20 | raise ValueError(f"No database adapter found for {uri}") 21 | -------------------------------------------------------------------------------- /docs/Synalinks API/Rewards/index.md: -------------------------------------------------------------------------------- 1 | # Rewards 2 | 3 | `Reward`s are an essential part of reinforcement learning frameworks. 4 | They are typically float values (usually between 0.0 and 1.0, but they can be 5 | negative also) that guide the process into making more efficient decisions or 6 | predictions. During training, the goal is to maximize the reward function. 7 | The reward gives the system an indication of how well it performed for that task. 8 | 9 | The purpose of a reward function is to compute the quantity that the program should maximize during training. 10 | 11 | # Rewards Overview 12 | 13 | - [ExactMatch reward](ExactMatch reward.md) 14 | - [CosineSimilarity reward](CosineSimilarity reward.md) 15 | - [LMAsJudge reward](LMAsJudge reward.md) 16 | - [ProgramAsJudge reward](Reward wrappers.md) -------------------------------------------------------------------------------- /synalinks/src/backend/common/global_state_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/backend/common/global_state_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src.backend.common import global_state 6 | from synalinks.src.testing import test_case 7 | from synalinks.src.utils.naming import auto_name 8 | 9 | 10 | class GlobalStateTest(test_case.TestCase): 11 | def test_clear_session(self): 12 | name0 = auto_name("somename") 13 | self.assertEqual(name0, "somename") 14 | name1 = auto_name("somename") 15 | self.assertEqual(name1, "somename_1") 16 | global_state.clear_session() 17 | name0 = auto_name("somename") 18 | self.assertEqual(name0, "somename") 19 | -------------------------------------------------------------------------------- /synalinks/api/tree/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.tree.tree_api import MAP_TO_NONE as MAP_TO_NONE 8 | from synalinks.src.tree.tree_api import assert_same_paths as assert_same_paths 9 | from synalinks.src.tree.tree_api import assert_same_structure as assert_same_structure 10 | from synalinks.src.tree.tree_api import flatten as flatten 11 | from synalinks.src.tree.tree_api import is_nested as is_nested 12 | from synalinks.src.tree.tree_api import lists_to_tuples as lists_to_tuples 13 | from synalinks.src.tree.tree_api import map_structure as map_structure 14 | from synalinks.src.tree.tree_api import pack_sequence_as as pack_sequence_as 15 | from synalinks.src.tree.tree_api import traverse as traverse 16 | -------------------------------------------------------------------------------- /synalinks/src/embedding_models/embedding_model_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from unittest.mock import patch 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import Embeddings 7 | from synalinks.src.embedding_models.embedding_model import EmbeddingModel 8 | 9 | 10 | class EmbeddingModelTest(testing.TestCase): 11 | @patch("litellm.aembedding") 12 | async def test_call_api(self, mock_embedding): 13 | embedding_model = EmbeddingModel(model="ollama/all-minilm") 14 | 15 | expected_value = [0.0, 0.1, 0.2, 0.3] 16 | mock_embedding.return_value = {"data": [{"embedding": expected_value}]} 17 | 18 | result = await embedding_model(["What is the capital of France?"]) 19 | self.assertEqual(result, Embeddings(**result).get_json()) 20 | self.assertEqual(result, {"embeddings": [expected_value]}) 21 | -------------------------------------------------------------------------------- /synalinks/api/ops/json/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.ops.json import concat as concat 8 | from synalinks.src.ops.json import concat as concatenate 9 | from synalinks.src.ops.json import factorize as factorize 10 | from synalinks.src.ops.json import in_mask as in_mask 11 | from synalinks.src.ops.json import logical_and as logical_and 12 | from synalinks.src.ops.json import logical_not as logical_not 13 | from synalinks.src.ops.json import logical_or as logical_or 14 | from synalinks.src.ops.json import logical_xor as logical_xor 15 | from synalinks.src.ops.json import out_mask as out_mask 16 | from synalinks.src.ops.json import prefix as prefix 17 | from synalinks.src.ops.json import suffix as suffix 18 | from synalinks.src.ops.language_models import predict as predict 19 | -------------------------------------------------------------------------------- /synalinks/src/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src import backend 2 | from synalinks.src import datasets 3 | from synalinks.src import embedding_models 4 | from synalinks.src import initializers 5 | from synalinks.src import language_models 6 | from synalinks.src import metrics 7 | from synalinks.src import modules 8 | from synalinks.src import ops 9 | from synalinks.src import optimizers 10 | from synalinks.src import programs 11 | from synalinks.src import rewards 12 | from synalinks.src import saving 13 | from synalinks.src import testing 14 | from synalinks.src import trainers 15 | from synalinks.src import tree 16 | from synalinks.src import utils 17 | from synalinks.src.backend import SymbolicDataModel 18 | from synalinks.src.modules import Input 19 | from synalinks.src.programs import Functional 20 | from synalinks.src.programs import Program 21 | from synalinks.src.programs import Sequential 22 | from synalinks.src.version import __version__ 23 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | permissions: 8 | contents: write 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Configure Git Credentials 15 | run: | 16 | git config user.name github-actions[bot] 17 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 18 | - uses: actions/setup-python@v5 19 | with: 20 | python-version: 3.x 21 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 22 | - uses: actions/cache@v4 23 | with: 24 | key: mkdocs-material-${{ env.cache_id }} 25 | path: .cache 26 | restore-keys: | 27 | mkdocs-material- 28 | - run: pip install mkdocs-material mkdocstrings[python] mkdocs-glightbox mkdocs-llmstxt 29 | - run: mkdocs gh-deploy --force -------------------------------------------------------------------------------- /docs/Synalinks API/Optimizers API/index.md: -------------------------------------------------------------------------------- 1 | # Optimizers API 2 | 3 | The `Optimizer`s are a key element in Synalinks, they updates the variables and backpropagate the rewards. 4 | 5 | They are in charge of modifying and optimizing the variables (including the prompts) of each individual module composing a synalinks program. 6 | 7 | ```mermaid 8 | graph LR 9 | A[Training Data] -->|Provide x:DataModel| B[Program]; 10 | B -->|Generate y_pred:JsonDataModel| C[Reward]; 11 | A -->|Provide y_true:DataModel| C; 12 | C -->|Compute reward:Float| D[Optimizer]; 13 | D -->|Update trainable_variable:Variable| B; 14 | ``` 15 | 16 | This reinforcement loop is what makes possible for the system to learn by 17 | repeatedly making predictions and refining its knowledge/methodology in order 18 | to maximize the reward. 19 | 20 | ## Optimizers API overview 21 | 22 | - [Base Optimizer class](Base Optimizer class.md) 23 | - [RandomFewShot optimizer](RandomFewShot.md) 24 | - [OMEGA optimizer](OMEGA.md) -------------------------------------------------------------------------------- /synalinks/api/callbacks/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.api.callbacks import monitor as monitor 8 | from synalinks.src.callbacks.backup_and_restore import ( 9 | BackupAndRestore as BackupAndRestore, 10 | ) 11 | from synalinks.src.callbacks.callback import Callback as Callback 12 | from synalinks.src.callbacks.callback_list import CallbackList as CallbackList 13 | from synalinks.src.callbacks.csv_logger import CSVLogger as CSVLogger 14 | from synalinks.src.callbacks.early_stopping import EarlyStopping as EarlyStopping 15 | from synalinks.src.callbacks.history import History as History 16 | from synalinks.src.callbacks.monitor import Monitor as Monitor 17 | from synalinks.src.callbacks.progbar_logger import ProgbarLogger as ProgbarLogger 18 | from synalinks.src.callbacks.program_checkpoint import ( 19 | ProgramCheckpoint as ProgramCheckpoint, 20 | ) 21 | -------------------------------------------------------------------------------- /synalinks/src/utils/plot_utils.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | import matplotlib.colors as mcolors 4 | import numpy as np 5 | from matplotlib import colormaps 6 | 7 | 8 | def generate_distinct_colors(n): 9 | """Generate n distinct colors, maximizing perceptual difference. 10 | Args: 11 | n (int): The number of colors to generate. 12 | Returns: 13 | list: List of RGBA colors. 14 | """ 15 | if n <= 20: 16 | # Use qualitative colormaps for small n 17 | cmap = colormaps["tab20"] 18 | colors = [cmap(i) for i in np.linspace(0, 1, n, endpoint=False)] 19 | else: 20 | # For larger n, sample hues in HSL space 21 | hues = np.linspace(0, 1, n, endpoint=False) 22 | colors = [] 23 | for hue in hues: 24 | # Convert HSL to RGB (fixed saturation and lightness) 25 | rgb = mcolors.hsv_to_rgb([hue, 0.9, 0.7]) 26 | colors.append(rgb) 27 | return colors 28 | -------------------------------------------------------------------------------- /synalinks/api/rewards/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.rewards import deserialize as deserialize 8 | from synalinks.src.rewards import get as get 9 | from synalinks.src.rewards import serialize as serialize 10 | from synalinks.src.rewards.cosine_similarity import CosineSimilarity as CosineSimilarity 11 | from synalinks.src.rewards.cosine_similarity import cosine_similarity as cosine_similarity 12 | from synalinks.src.rewards.exact_match import ExactMatch as ExactMatch 13 | from synalinks.src.rewards.exact_match import exact_match as exact_match 14 | from synalinks.src.rewards.lm_as_judge import LMAsJudge as LMAsJudge 15 | from synalinks.src.rewards.reward import Reward as Reward 16 | from synalinks.src.rewards.reward_wrappers import ProgramAsJudge as ProgramAsJudge 17 | from synalinks.src.rewards.reward_wrappers import ( 18 | RewardFunctionWrapper as RewardFunctionWrapper, 19 | ) 20 | -------------------------------------------------------------------------------- /synalinks/src/backend/common/symbolic_scope.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/backend/common/symbolic_scope.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src.api_export import synalinks_export 6 | from synalinks.src.backend.common import global_state 7 | 8 | 9 | @synalinks_export("synalinks.SymbolicScope") 10 | class SymbolicScope: 11 | """Scope to indicate the symbolic stage.""" 12 | 13 | def __enter__(self): 14 | self.original_scope = get_symbolic_scope() 15 | global_state.set_global_attribute("symbolic_scope", self) 16 | return self 17 | 18 | def __exit__(self, *args, **kwargs): 19 | global_state.set_global_attribute("symbolic_scope", self.original_scope) 20 | 21 | 22 | def in_symbolic_scope(): 23 | return global_state.get_global_attribute("symbolic_scope") is not None 24 | 25 | 26 | def get_symbolic_scope(): 27 | return global_state.get_global_attribute("symbolic_scope") 28 | -------------------------------------------------------------------------------- /synalinks/src/testing/test_case.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/testing/test_case.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import shutil 6 | import tempfile 7 | import unittest 8 | 9 | from absl.testing import parameterized 10 | 11 | from synalinks.src.backend.common.global_state import clear_session 12 | from synalinks.src.backend.config import disable_telemetry 13 | 14 | 15 | class TestCase( 16 | unittest.IsolatedAsyncioTestCase, parameterized.TestCase, unittest.TestCase 17 | ): 18 | maxDiff = None 19 | 20 | def __init__(self, *args, **kwargs): 21 | super().__init__(*args, **kwargs) 22 | 23 | def setUp(self): 24 | # clear global state so that test cases are independent 25 | clear_session(free_memory=False) 26 | disable_telemetry() 27 | 28 | def get_temp_dir(self): 29 | temp_dir = tempfile.mkdtemp() 30 | self.addCleanup(lambda: shutil.rmtree(temp_dir)) 31 | return temp_dir 32 | -------------------------------------------------------------------------------- /docs/Synalinks API/Built-in Datasets/index.md: -------------------------------------------------------------------------------- 1 | # Built-in Datasets 2 | 3 | The `synalinks.datasets` module provide a few datasets that can be used for debugging, evaluation or to create code examples. 4 | 5 | These datasets are leaked in nowadays LMs training data, which is a big concern in todays ML community, so they won't give you much information about the reasoning abilities of the underlying models. But they are still useful as baseline to compare neuro-symbolic methods or when using small language models. 6 | 7 | --- 8 | 9 | - [GSM8K dataset](GSM8K.md): A dataset of 8.5K high quality linguistically diverse grade school math word problems. Useful to evaluate reasoning capabilities. 10 | 11 | - [HotpotQA](HotpotQA.md): A dataset of 113k wikipedia-based question/answer pairs that need multiple documents to answer. This dataset is useful to evaluate Agentic RAGs or KnowledgeGraph RAGs with multi-hop. 12 | 13 | - [ARC-AGI](ARC-AGI.md): A challenging dataset containing tasks based on core knowledge principles to evaluate reasoning and program synthesis systems. -------------------------------------------------------------------------------- /coverage-badge.svg: -------------------------------------------------------------------------------- 1 | coverage: 71.25%coverage71.25% -------------------------------------------------------------------------------- /examples/conversational_application/conversational_application.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | 4 | from synalinks.backend import ChatMessages 5 | 6 | synalinks.enable_logging() 7 | 8 | 9 | async def main(): 10 | language_model = synalinks.LanguageModel( 11 | model="ollama/mistral", 12 | ) 13 | 14 | inputs = synalinks.Input(data_model=ChatMessages) 15 | outputs = await synalinks.Generator( 16 | language_model=language_model, 17 | prompt_template=synalinks.chat_prompt_template(), 18 | streaming=True, 19 | )(inputs) 20 | 21 | program = synalinks.Program( 22 | inputs=inputs, 23 | outputs=outputs, 24 | name="simple_chatbot", 25 | description="A simple conversation application", 26 | ) 27 | 28 | synalinks.utils.plot_program( 29 | program, 30 | to_folder="examples/conversational_application", 31 | show_module_names=True, 32 | show_trainable=True, 33 | show_schemas=True, 34 | ) 35 | 36 | 37 | if __name__ == "__main__": 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /synalinks/src/modules/core/input_module_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.backend import SymbolicDataModel 6 | from synalinks.src.backend import standardize_schema 7 | from synalinks.src.modules import Input 8 | from synalinks.src.modules import InputModule 9 | 10 | 11 | class InputLayerTest(testing.TestCase): 12 | def test_input_basic(self): 13 | class Query(DataModel): 14 | query: str 15 | 16 | values = InputModule(input_data_model=SymbolicDataModel(data_model=Query)) 17 | self.assertEqual(values.name, "input_module") 18 | self.assertEqual(values.get_schema(), standardize_schema(Query.get_schema())) 19 | 20 | def test_input(self): 21 | class Query(DataModel): 22 | query: str 23 | 24 | inputs = Input(data_model=Query) 25 | self.assertIsInstance(inputs, SymbolicDataModel) 26 | self.assertEqual(inputs.get_schema(), standardize_schema(Query.get_schema())) 27 | -------------------------------------------------------------------------------- /synalinks/api/datasets/arcagi/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.datasets.arcagi import default_instructions as default_instructions 8 | from synalinks.src.datasets.arcagi import ( 9 | get_arcagi1_evaluation_task_names as get_arcagi1_evaluation_task_names, 10 | ) 11 | from synalinks.src.datasets.arcagi import ( 12 | get_arcagi1_training_task_names as get_arcagi1_training_task_names, 13 | ) 14 | from synalinks.src.datasets.arcagi import ( 15 | get_arcagi2_evaluation_task_names as get_arcagi2_evaluation_task_names, 16 | ) 17 | from synalinks.src.datasets.arcagi import ( 18 | get_arcagi2_training_task_names as get_arcagi2_training_task_names, 19 | ) 20 | from synalinks.src.datasets.arcagi import get_input_data_model as get_input_data_model 21 | from synalinks.src.datasets.arcagi import get_output_data_model as get_output_data_model 22 | from synalinks.src.datasets.arcagi import load_data as load_data 23 | from synalinks.src.datasets.arcagi import plot_task as plot_task 24 | -------------------------------------------------------------------------------- /synalinks/api/saving/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.saving.object_registration import ( 8 | CustomObjectScope as CustomObjectScope, 9 | ) 10 | from synalinks.src.saving.object_registration import ( 11 | CustomObjectScope as custom_object_scope, 12 | ) 13 | from synalinks.src.saving.object_registration import ( 14 | get_custom_objects as get_custom_objects, 15 | ) 16 | from synalinks.src.saving.object_registration import ( 17 | get_registered_name as get_registered_name, 18 | ) 19 | from synalinks.src.saving.object_registration import ( 20 | get_registered_object as get_registered_object, 21 | ) 22 | from synalinks.src.saving.object_registration import ( 23 | register_synalinks_serializable as register_synalinks_serializable, 24 | ) 25 | from synalinks.src.saving.serialization_lib import ( 26 | deserialize_synalinks_object as deserialize_synalinks_object, 27 | ) 28 | from synalinks.src.saving.serialization_lib import ( 29 | serialize_synalinks_object as serialize_synalinks_object, 30 | ) 31 | -------------------------------------------------------------------------------- /docs/Synalinks API/Callbacks API/index.md: -------------------------------------------------------------------------------- 1 | # Callbacks API 2 | 3 | A callback is an object that can perform various actions at multiple stages of the program's training. 4 | For example, at the start or end of an epoch, before or after a single batch, etc. 5 | 6 | ## How to use Callbacks 7 | 8 | You can pass a list of callbacks to the `.fit()` method of a program. 9 | 10 | ```python 11 | import synalinks 12 | import asyncio 13 | 14 | async def main(): 15 | # ... you program declaration here 16 | 17 | callbacks = [ 18 | synalinks.callbacks.CSVLogger(filepath="training_log.csv"), 19 | synalinks.callbacks.ProgramCheckpoint( 20 | filepath="program.{epoch:02d}-{val_loss:.2f}.json" 21 | ), 22 | ] 23 | 24 | history = await program.fit( 25 | x=x_train, 26 | y=y_train, 27 | epochs=10, 28 | callbacks=callbacks, 29 | ) 30 | 31 | if __main__ == "__main__": 32 | asyncio.run(main()) 33 | ``` 34 | 35 | ## Callbacks Overview 36 | 37 | - [Base Callback class](Base Callback class.md) 38 | - [CSVLogger callback](CSVLogger.md) 39 | - [ProgramCheckPoint callback](ProgramCheckpoint.md) 40 | - [Monitor callback](Monitor.md) -------------------------------------------------------------------------------- /synalinks/src/api_export.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/api_export.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import namex 6 | 7 | # These dicts reference "canonical names" only 8 | # (i.e. the first name an object was registered with). 9 | REGISTERED_NAMES_TO_OBJS = {} 10 | REGISTERED_OBJS_TO_NAMES = {} 11 | 12 | 13 | def register_internal_serializable(path, symbol): 14 | global REGISTERED_NAMES_TO_OBJS 15 | if isinstance(path, (list, tuple)): 16 | name = path[0] 17 | else: 18 | name = path 19 | REGISTERED_NAMES_TO_OBJS[name] = symbol 20 | REGISTERED_OBJS_TO_NAMES[symbol] = name 21 | 22 | 23 | def get_symbol_from_name(name): 24 | return REGISTERED_NAMES_TO_OBJS.get(name, None) 25 | 26 | 27 | def get_name_from_symbol(symbol): 28 | return REGISTERED_OBJS_TO_NAMES.get(symbol, None) 29 | 30 | 31 | class synalinks_export(namex.export): 32 | def __init__(self, path): 33 | super().__init__(package="synalinks", path=path) 34 | 35 | def __call__(self, symbol): 36 | register_internal_serializable(self.path, symbol) 37 | return super().__call__(symbol) 38 | -------------------------------------------------------------------------------- /synalinks/api/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.metrics import deserialize as deserialize 8 | from synalinks.src.metrics import get as get 9 | from synalinks.src.metrics import serialize as serialize 10 | from synalinks.src.metrics.f_score_metrics import BinaryF1Score as BinaryF1Score 11 | from synalinks.src.metrics.f_score_metrics import BinaryFBetaScore as BinaryFBetaScore 12 | from synalinks.src.metrics.f_score_metrics import F1Score as F1Score 13 | from synalinks.src.metrics.f_score_metrics import FBetaScore as FBetaScore 14 | from synalinks.src.metrics.f_score_metrics import ListF1Score as ListF1Score 15 | from synalinks.src.metrics.f_score_metrics import ListFBetaScore as ListFBetaScore 16 | from synalinks.src.metrics.metric import Metric as Metric 17 | from synalinks.src.metrics.reduction_metrics import Mean as Mean 18 | from synalinks.src.metrics.reduction_metrics import MeanMetricWrapper as MeanMetricWrapper 19 | from synalinks.src.metrics.reduction_metrics import Sum as Sum 20 | from synalinks.src.metrics.regression_metrics import CosineSimilarity as CosineSimilarity 21 | -------------------------------------------------------------------------------- /synalinks/src/callbacks/history.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/callbacks/history.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src.api_export import synalinks_export 6 | from synalinks.src.callbacks.callback import Callback 7 | 8 | 9 | @synalinks_export("synalinks.callbacks.History") 10 | class History(Callback): 11 | """Callback that records events into a `History` object. 12 | 13 | This callback is automatically applied to 14 | every Synalinks program. The `History` object 15 | gets returned by the `fit()` method of programs. 16 | """ 17 | 18 | def __init__(self): 19 | super().__init__() 20 | self.history = {} 21 | 22 | def on_train_begin(self, logs=None): 23 | self.epoch = [] 24 | 25 | def on_epoch_end(self, epoch, logs=None): 26 | logs = logs or {} 27 | self.epoch.append(epoch) 28 | for k, v in logs.items(): 29 | self.history.setdefault(k, []).append(v) 30 | 31 | # Set the history attribute on the program after the epoch ends. This will 32 | # make sure that the state which is set is the latest one. 33 | self.program.history = self 34 | -------------------------------------------------------------------------------- /synalinks/src/initializers/empty_initializer_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from typing import List 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import DataModel 7 | from synalinks.src.initializers.empty_initializer import Empty 8 | 9 | 10 | class EmptyInitializerTest(testing.TestCase): 11 | def test_empty_initializer(self): 12 | class Instructions(DataModel): 13 | instructions: List[str] = [] 14 | 15 | initializer = Empty(data_model=Instructions) 16 | empty_data_model = initializer() 17 | self.assertEqual(initializer.get_schema(), Instructions.get_schema()) 18 | self.assertEqual(empty_data_model, Instructions().get_json()) 19 | 20 | def test_empty_initializer_from_config(self): 21 | class Instructions(DataModel): 22 | instructions: List[str] = [] 23 | 24 | initializer = Empty(data_model=Instructions) 25 | config = initializer.get_config() 26 | initializer = Empty.from_config(config) 27 | empty_data_model = initializer() 28 | self.assertEqual(initializer.get_schema(), Instructions.get_schema()) 29 | self.assertEqual(empty_data_model, Instructions().get_json()) 30 | -------------------------------------------------------------------------------- /docs/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /* Light mode */ 2 | [data-md-color-scheme="default"] { 3 | --md-default-bg-color: #ffffff; 4 | --md-default-fg-color: #002c37; 5 | 6 | --md-primary-bg-color: #ffffff; 7 | --md-primary-fg-color: #0a3642; 8 | --md-primary-fg-color--dark: #002c37; 9 | --md-primary-fg-color--light: #0a3642; 10 | 11 | --md-accent-fg-color: #70a0a0; 12 | 13 | --md-code-fg-color: #002c37; 14 | --md-code-bg-color: #ffffff; 15 | 16 | --md-footer-fg-color: #303030; 17 | --md-footer-bg-color: #ffffff; 18 | --md-footer-fg-color--light: #303030; 19 | --md-footer-bg-color--dark: #ffffff; 20 | } 21 | 22 | /* Dark mode */ 23 | [data-md-color-scheme="slate"] { 24 | --md-default-bg-color: #1a1a1a; 25 | --md-default-fg-color: #ffffff; 26 | 27 | --md-primary-bg-color: #ffffff; 28 | --md-primary-fg-color: #1a1a1a; 29 | --md-primary-fg-color--dark: #0a3642; 30 | --md-primary-fg-color--light: #303030; 31 | 32 | --md-accent-fg-color: #70a0a0; 33 | --md-accent-bg-color: #0a3642; 34 | 35 | --md-code-fg-color: #ffffff; 36 | --md-code-bg-color: #303030; 37 | 38 | --md-footer-fg-color: #ffffff; 39 | --md-footer-bg-color: #1a1a1a; 40 | --md-footer-fg-color--light: #ffffff; 41 | --md-footer-bg-color--dark: #1a1a1a; 42 | } -------------------------------------------------------------------------------- /synalinks/src/trainers/epoch_iterator_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/trainers/epoch_iterator_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import numpy as np 6 | 7 | from synalinks.src import testing 8 | from synalinks.src.testing.test_utils import AnswerWithRationale 9 | from synalinks.src.testing.test_utils import Query 10 | from synalinks.src.testing.test_utils import load_test_data 11 | from synalinks.src.trainers.epoch_iterator import EpochIterator 12 | 13 | 14 | class EpochIteratorTest(testing.TestCase): 15 | def test_basic_flow(self): 16 | (x_train, y_train), (x_test, y_test) = load_test_data() 17 | 18 | epoch_iterator = EpochIterator( 19 | x=x_train, 20 | y=y_train, 21 | batch_size=1, 22 | ) 23 | 24 | with epoch_iterator.catch_stop_iteration(): 25 | for step, iterator in epoch_iterator: 26 | data = iterator[0] 27 | x_batch, y_batch = data 28 | self.assertIsInstance(x_batch, np.ndarray) 29 | self.assertIsInstance(y_batch, np.ndarray) 30 | self.assertIsInstance(x_batch[0], Query) 31 | self.assertIsInstance(y_batch[0], AnswerWithRationale) 32 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_and.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import ops 4 | from synalinks.src.api_export import synalinks_export 5 | from synalinks.src.modules.module import Module 6 | 7 | 8 | @synalinks_export( 9 | [ 10 | "synalinks.And", 11 | "synalinks.modules.And", 12 | ] 13 | ) 14 | class And(Module): 15 | """Perform a logical And operation. 16 | 17 | It takes as input a list of data models, 18 | and returns a concatenation of them. 19 | 20 | If any input is None, then it output None. 21 | 22 | Table: 23 | 24 | | `x1` | `x2` | Logical And (`&`) | 25 | | ------ | ------ | ----------------- | 26 | | `x1` | `x2` | `x1 + x2` | 27 | | `x1` | `None` | `None` | 28 | | `None` | `x2` | `None` | 29 | | `None` | `None` | `None` | 30 | 31 | Args: 32 | **kwargs (keyword arguments): Standard keyword arguments for the module. 33 | """ 34 | 35 | def __init__(self, **kwargs): 36 | super().__init__(**kwargs) 37 | 38 | async def call(self, inputs, training=False): 39 | output = inputs[0] 40 | for i in range(1, len(inputs)): 41 | output = await ops.logical_and( 42 | output, 43 | inputs[i], 44 | name=f"module_and_{i}_" + self.name, 45 | ) 46 | return output 47 | -------------------------------------------------------------------------------- /examples/json_operators/logical_and.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | 3 | synalinks.enable_logging() 4 | 5 | # Logical AND with Synalinks 6 | 7 | 8 | class Query(synalinks.DataModel): 9 | query: str 10 | 11 | 12 | class Answer(synalinks.DataModel): 13 | answer: str 14 | 15 | 16 | qa_pair = Query & Answer 17 | 18 | assert isinstance(qa_pair, synalinks.SymbolicDataModel) 19 | 20 | print(qa_pair.prettify_schema()) 21 | # { 22 | # "additionalProperties": false, 23 | # "properties": { 24 | # "query": { 25 | # "title": "Query", 26 | # "type": "string" 27 | # }, 28 | # "answer": { 29 | # "title": "Answer", 30 | # "type": "string" 31 | # } 32 | # }, 33 | # "required": [ 34 | # "query", 35 | # "answer" 36 | # ], 37 | # "title": "Query", 38 | # "type": "object" 39 | # } 40 | 41 | # When performing an And operation with `None` the output is `None` 42 | # You can see the logical And as a robust concatenation operation. 43 | 44 | qa_pair = Query(query="Why is neuro-symbolic AI powering the next wave?") & None 45 | 46 | assert isinstance(qa_pair, None) 47 | 48 | # Here is the table summarizing the behavior 49 | 50 | # Truth Table: 51 | 52 | # | `x1` | `x2` | Logical And (`&`) | 53 | # | ------ | ------ | ----------------- | 54 | # | `x1` | `x2` | `x1 + x2` | 55 | # | `x1` | `None` | `None` | 56 | # | `None` | `x2` | `None` | 57 | # | `None` | `None` | `None` | 58 | -------------------------------------------------------------------------------- /examples/agents/mcp_tool_agent/mcp_server.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any 2 | from fastmcp import FastMCP 3 | 4 | mcp = FastMCP("Demo 🚀") 5 | 6 | 7 | @mcp.tool 8 | async def calculate(expression: str) -> Dict[str, Any]: 9 | """Calculate the result of a mathematical expression. 10 | 11 | Args: 12 | expression (str): The mathematical expression to calculate, such as 13 | '2 + 2'. The expression can contain numbers, operators (+, -, *, /), 14 | parentheses, and spaces. 15 | """ 16 | if not all(char in "0123456789+-*/(). " for char in expression): 17 | return { 18 | "result": None, 19 | "log": ( 20 | "Error: invalid characters in expression. " 21 | "The expression can only contain numbers, operators (+, -, *, /)," 22 | " parentheses, and spaces NOT letters." 23 | ), 24 | } 25 | try: 26 | # Evaluate the mathematical expression safely 27 | result = round(float(eval(expression, {"__builtins__": None}, {})), 2) 28 | return { 29 | "result": result, 30 | "log": "Successfully executed", 31 | } 32 | except Exception as e: 33 | return { 34 | "result": None, 35 | "log": f"Error: {e}", 36 | } 37 | 38 | 39 | if __name__ == "__main__": 40 | mcp.run( 41 | transport="http", 42 | host="127.0.0.1", 43 | port=8183, 44 | ) 45 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_or.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import ops 4 | from synalinks.src.api_export import synalinks_export 5 | from synalinks.src.modules.module import Module 6 | 7 | 8 | @synalinks_export( 9 | [ 10 | "synalinks.Or", 11 | "synalinks.modules.Or", 12 | ] 13 | ) 14 | class Or(Module): 15 | """Perform a logical Or operation. 16 | 17 | It takes as input a list of data models, 18 | and returns a concatenation of them (if all are provided) 19 | otherwise it output the one that is not None. 20 | 21 | If any input is None, it is ignored. 22 | 23 | Table: 24 | 25 | | `x1` | `x2` | Logical Or (`|`) | 26 | | ------ | ------ | ---------------- | 27 | | `x1` | `x2` | `x1 + x2` | 28 | | `x1` | `None` | `x1` | 29 | | `None` | `x2` | `x2` | 30 | | `None` | `None` | `None` | 31 | 32 | Args: 33 | **kwargs (keyword arguments): Standard keyword arguments for the module. 34 | """ 35 | 36 | def __init__(self, **kwargs): 37 | super().__init__(**kwargs) 38 | 39 | async def call(self, inputs, training=False): 40 | output = inputs[0] 41 | for i in range(1, len(inputs)): 42 | output = await ops.logical_or( 43 | output, 44 | inputs[i], 45 | name=f"module_or_{i}_" + self.name, 46 | ) 47 | return output 48 | -------------------------------------------------------------------------------- /synalinks/src/trainers/data_adapters/__init__.py: -------------------------------------------------------------------------------- 1 | import types 2 | 3 | from synalinks.src.trainers.data_adapters import array_data_adapter 4 | from synalinks.src.trainers.data_adapters import data_adapter 5 | from synalinks.src.trainers.data_adapters.array_data_adapter import ArrayDataAdapter 6 | from synalinks.src.trainers.data_adapters.generator_data_adapter import ( 7 | GeneratorDataAdapter, 8 | ) 9 | 10 | 11 | def get_data_adapter( 12 | x, 13 | y=None, 14 | batch_size=None, 15 | steps_per_epoch=None, 16 | shuffle=False, 17 | ): 18 | # Allow passing a custom data adapter. 19 | if isinstance(x, data_adapter.DataAdapter): 20 | return x 21 | 22 | if array_data_adapter.can_convert_arrays((x, y)): 23 | return ArrayDataAdapter( 24 | x, 25 | y, 26 | shuffle=shuffle, 27 | batch_size=batch_size, 28 | steps=steps_per_epoch, 29 | ) 30 | elif isinstance(x, types.GeneratorType): 31 | if y is not None: 32 | raise_unsupported_arg("y", "the targets", "PyDataset") 33 | return GeneratorDataAdapter(x) 34 | 35 | else: 36 | raise ValueError(f"Unrecognized data type: x={x} (of type {type(x)})") 37 | 38 | 39 | def raise_unsupported_arg(arg_name, arg_description, input_type): 40 | raise ValueError( 41 | f"When providing `x` as a {input_type}, `{arg_name}` " 42 | f"should not be passed. Instead, {arg_description} should " 43 | f"be included as part of the {input_type}." 44 | ) 45 | -------------------------------------------------------------------------------- /synalinks/api/ops/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.api.ops import embedding_models as embedding_models 8 | from synalinks.api.ops import json as json 9 | from synalinks.api.ops import knowledge_bases as knowledge_bases 10 | from synalinks.src.backend import convert_to_json_data_model as convert_to_json_data_model 11 | from synalinks.src.backend import ( 12 | convert_to_symbolic_data_model as convert_to_symbolic_data_model, 13 | ) 14 | from synalinks.src.ops.embedding_models import embedding as embedding 15 | from synalinks.src.ops.json import concat as concat 16 | from synalinks.src.ops.json import concat as concatenate 17 | from synalinks.src.ops.json import factorize as factorize 18 | from synalinks.src.ops.json import in_mask as in_mask 19 | from synalinks.src.ops.json import logical_and as logical_and 20 | from synalinks.src.ops.json import logical_not as logical_not 21 | from synalinks.src.ops.json import logical_or as logical_or 22 | from synalinks.src.ops.json import logical_xor as logical_xor 23 | from synalinks.src.ops.json import out_mask as out_mask 24 | from synalinks.src.ops.json import prefix as prefix 25 | from synalinks.src.ops.json import suffix as suffix 26 | from synalinks.src.ops.knowledge_bases import similarity_search as similarity_search 27 | from synalinks.src.ops.knowledge_bases import update_knowledge as update_knowledge 28 | from synalinks.src.ops.language_models import predict as predict 29 | -------------------------------------------------------------------------------- /synalinks/src/initializers/empty_initializer.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src.api_export import synalinks_export 4 | from synalinks.src.backend import is_meta_class 5 | from synalinks.src.initializers.initializer import Initializer 6 | 7 | 8 | @synalinks_export( 9 | [ 10 | "synalinks.initializers.Empty", 11 | "synalinks.initializers.empty", 12 | ] 13 | ) 14 | class Empty(Initializer): 15 | """ 16 | Initialize a variable with the data_model default value. 17 | 18 | Args: 19 | schema (dict): The JSON object schema. If not provided, 20 | uses the `data_model` to infer it. 21 | value (dict): The initial JSON object. If the value is not provided the 22 | `data_model` should be provided. When a value is provided, a schema 23 | should be provided. 24 | data_model (DataModel): The backend data_model to use. 25 | 26 | """ 27 | 28 | def get_config(self): 29 | return { 30 | "schema": self._schema, 31 | "json": self._json, 32 | } 33 | 34 | def __call__(self, data_model=None): 35 | """Returns a JSON object initialized as specified by the initializer.""" 36 | if data_model: 37 | if not is_meta_class(data_model): 38 | self._json = data_model.get_json() 39 | else: 40 | self._json = data_model().get_json() 41 | self._schema = data_model.get_schema() 42 | return self._json 43 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/concat.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import ops 4 | from synalinks.src.api_export import synalinks_export 5 | from synalinks.src.modules.module import Module 6 | 7 | 8 | @synalinks_export( 9 | [ 10 | "synalinks.Concat", 11 | "synalinks.Concatenate", 12 | "synalinks.modules.Concat", 13 | "synalinks.modules.Concatenate", 14 | ] 15 | ) 16 | class Concat(Module): 17 | """Perform a concatenation operation. 18 | 19 | It takes as input a list of data models, 20 | and returns a concatenation of them. 21 | 22 | If any input is None, an exception is raised. 23 | 24 | Table: 25 | 26 | | `x1` | `x2` | Concat (`+`) | 27 | | ------ | ------ | ----------------- | 28 | | `x1` | `x2` | `x1 + x2` | 29 | | `x1` | `None` | `Exception` | 30 | | `None` | `x2` | `Exception` | 31 | | `None` | `None` | `Exception` | 32 | 33 | Args: 34 | **kwargs (keyword arguments): Standard keyword arguments for the module. 35 | """ 36 | 37 | def __init__(self, **kwargs): 38 | super().__init__(**kwargs) 39 | 40 | async def call(self, inputs, training=False): 41 | output = inputs[0] 42 | for i in range(1, len(inputs)): 43 | output = await ops.concat( 44 | output, 45 | inputs[i], 46 | name=f"module_concat_{i}_" + self.name, 47 | ) 48 | return output 49 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_xor.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src.api_export import synalinks_export 4 | from synalinks.src.modules.module import Module 5 | 6 | 7 | @synalinks_export( 8 | [ 9 | "synalinks.Xor", 10 | "synalinks.modules.Xor", 11 | ] 12 | ) 13 | class Xor(Module): 14 | """Perform a logical Xor operation. 15 | 16 | It takes as input a list of data models, 17 | If more than two data models are not None, then it output None. 18 | otherwise it output the one that is not None. 19 | 20 | Table: 21 | 22 | | `x1` | `x2` | Logical Xor (`^`)| 23 | | ------ | ------ | ---------------- | 24 | | `x1` | `x2` | `None` | 25 | | `x1` | `None` | `x1` | 26 | | `None` | `x2` | `x2` | 27 | | `None` | `None` | `None` | 28 | 29 | Args: 30 | **kwargs (keyword arguments): Standard keyword arguments for the module. 31 | """ 32 | 33 | def __init__(self, **kwargs): 34 | super().__init__(**kwargs) 35 | 36 | async def compute_output_spec(self, inputs, training=False): 37 | return inputs[0].clone() 38 | 39 | async def call(self, inputs, training=False): 40 | output = inputs[0] 41 | for i in range(1, len(inputs)): 42 | if inputs[i]: 43 | if not output: 44 | output = inputs[i] 45 | else: 46 | return None 47 | return output.clone(name=self.name) 48 | -------------------------------------------------------------------------------- /synalinks/src/modules/core/not_module.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import tree 4 | from synalinks.src.api_export import synalinks_export 5 | from synalinks.src.backend import JsonDataModel 6 | from synalinks.src.backend import SymbolicDataModel 7 | from synalinks.src.modules.module import Module 8 | 9 | 10 | @synalinks_export(["synalinks.modules.Not", "synalinks.Not"]) 11 | class Not(Module): 12 | """Not module. 13 | 14 | This module should be used as a placeholder when no operation is to be 15 | performed and the output should be None. 16 | 17 | This module is useful to implement stop conditions when combined with a conditional 18 | branch or as placeholder (like the Identity) before implementing guards that leverage 19 | the xor operation. 20 | 21 | Args: 22 | **kwargs (keyword arguments): The default module's arguments 23 | """ 24 | 25 | def __init__(self, **kwargs): 26 | super().__init__(**kwargs) 27 | self.built = True 28 | 29 | async def call(self, inputs): 30 | if isinstance(inputs, (JsonDataModel, SymbolicDataModel)): 31 | return None 32 | return tree.map_structure( 33 | lambda x: None, 34 | inputs, 35 | ) 36 | 37 | async def compute_output_spec(self, inputs): 38 | if isinstance(inputs, (JsonDataModel, SymbolicDataModel)): 39 | return inputs.clone() 40 | return tree.map_structure( 41 | lambda x: x.clone(), 42 | inputs, 43 | ) 44 | -------------------------------------------------------------------------------- /synalinks/src/utils/module_utils.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/utils/module_utils.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import importlib 6 | 7 | 8 | class LazyModule: 9 | def __init__(self, name, pip_name=None): 10 | self.name = name 11 | pip_name = pip_name or name 12 | self.pip_name = pip_name 13 | self.module = None 14 | self._available = None 15 | 16 | @property 17 | def available(self): 18 | if self._available is None: 19 | try: 20 | self.initialize() 21 | self._available = True 22 | except ImportError: 23 | self._available = False 24 | return self._available 25 | 26 | def initialize(self): 27 | try: 28 | self.module = importlib.import_module(self.name) 29 | except ImportError: 30 | raise ImportError( 31 | f"This requires the {self.name} module. " 32 | f"You can install it via `pip install {self.pip_name}`" 33 | ) 34 | 35 | def __getattr__(self, name): 36 | if name == "_api_export_path": 37 | raise AttributeError 38 | if self.module is None: 39 | self.initialize() 40 | return getattr(self.module, name) 41 | 42 | def __repr__(self): 43 | return f"LazyModule({self.name})" 44 | 45 | 46 | scipy = LazyModule("scipy") 47 | matplotlib = LazyModule("matplotlib") 48 | optree = LazyModule("optree") 49 | -------------------------------------------------------------------------------- /synalinks/src/ops/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.backend import convert_to_json_data_model 2 | from synalinks.src.backend import name_scope 3 | from synalinks.src.ops.embedding_models import Embedding 4 | from synalinks.src.ops.embedding_models import embedding 5 | from synalinks.src.ops.json import And 6 | from synalinks.src.ops.json import Concat 7 | from synalinks.src.ops.json import Factorize 8 | from synalinks.src.ops.json import InMask 9 | from synalinks.src.ops.json import Not 10 | from synalinks.src.ops.json import Or 11 | from synalinks.src.ops.json import OutMask 12 | from synalinks.src.ops.json import Xor 13 | from synalinks.src.ops.json import concat 14 | from synalinks.src.ops.json import factorize 15 | from synalinks.src.ops.json import in_mask 16 | from synalinks.src.ops.json import logical_and 17 | from synalinks.src.ops.json import logical_not 18 | from synalinks.src.ops.json import logical_or 19 | from synalinks.src.ops.json import logical_xor 20 | from synalinks.src.ops.json import out_mask 21 | from synalinks.src.ops.json import prefix 22 | from synalinks.src.ops.json import suffix 23 | from synalinks.src.ops.knowledge_bases import SimilaritySearch 24 | from synalinks.src.ops.knowledge_bases import TripletSearch 25 | from synalinks.src.ops.knowledge_bases import UpdateKnowledge 26 | from synalinks.src.ops.knowledge_bases import similarity_search 27 | from synalinks.src.ops.knowledge_bases import triplet_search 28 | from synalinks.src.ops.knowledge_bases import update_knowledge 29 | from synalinks.src.ops.language_models import Predict 30 | from synalinks.src.ops.language_models import predict 31 | -------------------------------------------------------------------------------- /synalinks/src/trainers/data_adapters/generator_data_adapter.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/trainers/data_adapters/generator_data_adapter.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import itertools 6 | 7 | from synalinks.src.trainers.data_adapters import data_adapter_utils 8 | from synalinks.src.trainers.data_adapters.data_adapter import DataAdapter 9 | 10 | 11 | class GeneratorDataAdapter(DataAdapter): 12 | """Adapter for Python generators.""" 13 | 14 | def __init__(self, generator): 15 | first_batches, generator = peek_and_restore(generator) 16 | self.generator = generator 17 | self._first_batches = first_batches 18 | self._output_signature = None 19 | if not isinstance(first_batches[0], tuple): 20 | raise ValueError( 21 | "When passing a Python generator to a Synalinks program, " 22 | "the generator must return a tuple, either " 23 | "(input,) or (inputs, targets). " 24 | f"Received: {first_batches[0]}" 25 | ) 26 | 27 | def get_numpy_iterator(self): 28 | return data_adapter_utils.get_numpy_iterator(self.generator()) 29 | 30 | @property 31 | def num_batches(self): 32 | return None 33 | 34 | @property 35 | def batch_size(self): 36 | return None 37 | 38 | 39 | def peek_and_restore(generator): 40 | batches = list(itertools.islice(generator, data_adapter_utils.NUM_BATCHES_FOR_SPEC)) 41 | return batches, lambda: itertools.chain(batches, generator) 42 | -------------------------------------------------------------------------------- /examples/agents/mcp_tool_agent/mcp_agent.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | 4 | synalinks.enable_logging() 5 | 6 | 7 | class Query(synalinks.DataModel): 8 | """Input query data model""" 9 | 10 | query: str = synalinks.Field( 11 | description="The user query", 12 | ) 13 | 14 | 15 | class FinalAnswer(synalinks.DataModel): 16 | """Final answer data model""" 17 | 18 | answer: str = synalinks.Field( 19 | description="The correct final answer", 20 | ) 21 | 22 | 23 | async def main(): 24 | language_model = synalinks.LanguageModel( 25 | model="ollama/mistral", 26 | ) 27 | 28 | mcp_client = synalinks.MultiServerMCPClient( 29 | { 30 | "math": { 31 | "url": "http://localhost:8183/mcp/", 32 | "transport": "streamable_http", 33 | }, 34 | } 35 | ) 36 | 37 | tools = await mcp_client.get_tools() 38 | 39 | inputs = synalinks.Input(data_model=Query) 40 | outputs = await synalinks.FunctionCallingAgent( 41 | data_model=FinalAnswer, 42 | tools=tools, 43 | language_model=language_model, 44 | max_iterations=5, 45 | autonomous=True, 46 | )(inputs) 47 | 48 | agent = synalinks.Program( 49 | inputs=inputs, 50 | outputs=outputs, 51 | name="mcp_math_agent", 52 | description="A math agent that can use an external calculator", 53 | ) 54 | 55 | input_query = Query(query="How much is 152648 + 485?") 56 | response = await agent(input_query) 57 | 58 | print(response.prettify_json()) 59 | 60 | 61 | if __name__ == "__main__": 62 | asyncio.run(main()) 63 | -------------------------------------------------------------------------------- /synalinks/src/callbacks/callback_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/callbacks/callback_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import DataModel 7 | from synalinks.src.callbacks import Callback 8 | from synalinks.src.optimizers.random_few_shot import RandomFewShot 9 | from synalinks.src.programs import Program 10 | from synalinks.src.rewards import ExactMatch 11 | 12 | 13 | class CallbackTest(testing.TestCase): 14 | def test_model_state_is_current_on_epoch_end(self): 15 | class Query(DataModel): 16 | query: str 17 | 18 | class Answer(DataModel): 19 | answer: str 20 | 21 | class Iterations(DataModel): 22 | count: int = 0 23 | 24 | class TestProgram(Program): 25 | def __init__(self): 26 | super().__init__() 27 | self.iterations = self.add_variable( 28 | data_model=Iterations, 29 | ) 30 | 31 | def call(self, inputs): 32 | self.iterations.json["count"] += 1 33 | return inputs 34 | 35 | class CBK(Callback): 36 | def on_batch_end(self, batch, logs): 37 | assert self.iterations.json["count"] == batch + 1 38 | 39 | model = TestProgram() 40 | model.compile(optimizer=RandomFewShot(), reward=ExactMatch()) 41 | x = [Query(query="test 1"), Query(query="test 2")] 42 | y = [Answer(answer="answer 1"), Answer(answer="answer 2")] 43 | model.fit(x, y, callbacks=[CBK()], batch_size=2) 44 | -------------------------------------------------------------------------------- /synalinks/src/utils/tool_utils_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import saving 4 | from synalinks.src import testing 5 | from synalinks.src.utils.tool_utils import Tool 6 | 7 | 8 | @saving.object_registration.register_synalinks_serializable() 9 | async def calculate(expression: str): 10 | """Calculate the result of a mathematical expression. 11 | 12 | Args: 13 | expression (str): The mathematical expression to calculate, such as 14 | '2 + 2'. The expression can contain numbers, operators (+, -, *, /), 15 | parentheses, and spaces. 16 | """ 17 | if not all(char in "0123456789+-*/(). " for char in expression): 18 | return { 19 | "result": None, 20 | "log": "Error: invalid characters in expression", 21 | } 22 | try: 23 | # Evaluate the mathematical expression safely 24 | result = round(float(eval(expression, {"__builtins__": None}, {})), 2) 25 | return { 26 | "result": result, 27 | "log": "Successfully executed", 28 | } 29 | except Exception as e: 30 | return { 31 | "result": None, 32 | "log": f"Error: {e}", 33 | } 34 | 35 | 36 | class ToolUtilsTest(testing.TestCase): 37 | def test_basic_tool(self): 38 | _ = Tool(calculate) 39 | 40 | async def test_tool_serialization(self): 41 | tool = Tool(calculate) 42 | tool_config = tool.get_config() 43 | new_tool = Tool.from_config(tool_config) 44 | 45 | tool_call = await new_tool("2+2") 46 | result = tool_call.get("result") 47 | 48 | self.assertTrue(result == 4) 49 | -------------------------------------------------------------------------------- /synalinks/src/language_models/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.api_export import synalinks_export 2 | from synalinks.src.language_models.language_model import LanguageModel 3 | from synalinks.src.saving import serialization_lib 4 | 5 | ALL_OBJECTS = { 6 | LanguageModel, 7 | } 8 | 9 | ALL_OBJECTS_DICT = {cls.__name__.lower(): cls for cls in ALL_OBJECTS} 10 | 11 | 12 | @synalinks_export("synalinks.language_models.serialize") 13 | def serialize(language_model): 14 | """Returns the optimizer configuration as a Python dict. 15 | 16 | Args: 17 | optimizer: An `LanguageModel` instance to serialize. 18 | 19 | Returns: 20 | Python dict which contains the configuration of the language model. 21 | """ 22 | return serialization_lib.serialize_synalinks_object(language_model) 23 | 24 | 25 | @synalinks_export("synalinks.language_models.deserialize") 26 | def deserialize(config, custom_objects=None): 27 | """Returns a Synalinks language model object via its configuration. 28 | 29 | Args: 30 | config: LanguageModel configuration dictionary. 31 | custom_objects: Optional dictionary mapping names (strings) to custom 32 | objects (classes and functions) to be considered during 33 | deserialization. 34 | 35 | Returns: 36 | A Synalinks LanguageModel instance. 37 | """ 38 | # Make deserialization case-insensitive for built-in language model. 39 | if config["class_name"].lower() in ALL_OBJECTS_DICT: 40 | config["class_name"] = config["class_name"].lower() 41 | 42 | return serialization_lib.deserialize_synalinks_object( 43 | config, 44 | module_objects=ALL_OBJECTS_DICT, 45 | custom_objects=custom_objects, 46 | ) 47 | -------------------------------------------------------------------------------- /docs/Monitoring/Arize Phoenix.md: -------------------------------------------------------------------------------- 1 | # Enabling LM Tracing with Arize Phoenix 2 | 3 | Monitoring is important for several reasons, especially in the context of machine learning and software development. It helps identify issues and bugs in your Synalinks programs by providing a detailed log of events and operations. This makes it easier to pinpoint where things went wrong and why. 4 | 5 | In this guide we are going to setup the tracing locally, for more information on how to setup in the cloud, refer to [Arize Phoenix documentation](https://docs.arize.com/phoenix) 6 | 7 | ```shell 8 | uv pip install openinference-instrumentation-litellm arize-otel 9 | ``` 10 | 11 | To activate the LM tracing, add the following lines to the top of your script 12 | 13 | ```python 14 | # Import open-telemetry dependencies 15 | from arize.otel import register 16 | from openinference.instrumentation.litellm import LiteLLMInstrumentor 17 | 18 | # Setup OTel via Arize Phoenix convenience function 19 | tracer_provider = register( 20 | space_id = "your-space-id", # in app space settings page 21 | api_key = "your-api-key", # in app space settings page 22 | project_name = "your-project-name", # name this to whatever you would like 23 | ) 24 | 25 | LiteLLMInstrumentor().instrument(tracer_provider=tracer_provider) 26 | ``` 27 | 28 | You are done, Arize Phoenix is now configured. 29 | 30 | To launch Arize Phoenix server, first pull the docker image with the following command. 31 | 32 | ```shell 33 | docker pull arizephoenix/phoenix 34 | ``` 35 | 36 | Then use the following command in a shell 37 | 38 | ```shell 39 | docker run -p 6006:6006 -p 4317:4317 -i -t arizephoenix/phoenix:latest 40 | ``` 41 | 42 | Finally go to `http://0.0.0.0:6006` to monitor your application. -------------------------------------------------------------------------------- /synalinks/src/modules/core/decision_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | import json 4 | from unittest.mock import patch 5 | 6 | from synalinks.src import testing 7 | from synalinks.src.backend import DataModel 8 | from synalinks.src.language_models import LanguageModel 9 | from synalinks.src.modules import Input 10 | from synalinks.src.modules.core.decision import Decision 11 | from synalinks.src.programs import Program 12 | 13 | 14 | class DecisionTest(testing.TestCase): 15 | @patch("litellm.acompletion") 16 | async def test_basic_decision(self, mock_completion): 17 | class Query(DataModel): 18 | query: str 19 | 20 | language_model = LanguageModel( 21 | model="ollama/mistral", 22 | ) 23 | 24 | expected_string = ( 25 | """{"thinking": "The question ask for the capital of France, """ 26 | """the answer is straitforward as it is well known that Paris is the""" 27 | """ capital", "choice": "easy"}""" 28 | ) 29 | 30 | mock_completion.return_value = { 31 | "choices": [{"message": {"content": expected_string}}] 32 | } 33 | 34 | x0 = Input(data_model=Query) 35 | x1 = await Decision( 36 | question="What is the difficulty level of the above provided query?", 37 | labels=["easy", "difficult", "unkown"], 38 | language_model=language_model, 39 | )(x0) 40 | 41 | program = Program( 42 | inputs=x0, 43 | outputs=x1, 44 | ) 45 | 46 | result = await program(Query(query="What is the French capital?")) 47 | 48 | self.assertEqual(result.get_json(), json.loads(expected_string)) 49 | -------------------------------------------------------------------------------- /synalinks/src/embedding_models/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.api_export import synalinks_export 2 | from synalinks.src.embedding_models.embedding_model import EmbeddingModel 3 | from synalinks.src.saving import serialization_lib 4 | 5 | ALL_OBJECTS = { 6 | EmbeddingModel, 7 | } 8 | 9 | ALL_OBJECTS_DICT = {cls.__name__.lower(): cls for cls in ALL_OBJECTS} 10 | 11 | 12 | @synalinks_export("synalinks.embedding_models.serialize") 13 | def serialize(embedding_model): 14 | """Returns the optimizer configuration as a Python dict. 15 | 16 | Args: 17 | optimizer: An `EmbeddingModel` instance to serialize. 18 | 19 | Returns: 20 | Python dict which contains the configuration of the language model. 21 | """ 22 | return serialization_lib.serialize_synalinks_object(embedding_model) 23 | 24 | 25 | @synalinks_export("synalinks.embedding_models.deserialize") 26 | def deserialize(config, custom_objects=None): 27 | """Returns a Synalinks language model object via its configuration. 28 | 29 | Args: 30 | config: EmbeddingModel configuration dictionary. 31 | custom_objects: Optional dictionary mapping names (strings) to custom 32 | objects (classes and functions) to be considered during 33 | deserialization. 34 | 35 | Returns: 36 | A Synalinks EmbeddingModel instance. 37 | """ 38 | # Make deserialization case-insensitive for built-in embedding model. 39 | if config["class_name"].lower() in ALL_OBJECTS_DICT: 40 | config["class_name"] = config["class_name"].lower() 41 | 42 | return serialization_lib.deserialize_synalinks_object( 43 | config, 44 | module_objects=ALL_OBJECTS_DICT, 45 | custom_objects=custom_objects, 46 | ) 47 | -------------------------------------------------------------------------------- /examples/json_operators/concatenate.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | 3 | synalinks.enable_logging() 4 | 5 | # Concatenation with Synalinks 6 | 7 | 8 | class Query(synalinks.DataModel): 9 | query: str 10 | 11 | 12 | class Answer(synalinks.DataModel): 13 | answer: str 14 | 15 | 16 | # Synalinks operators works at a metaclass level 17 | # In that case, the result is a `SymbolicDataModel` 18 | # A `SymbolicDataModel` can be understand as a data 19 | # specification/contract. It only contains a JSON schema 20 | # and cannot be used for computation. It allow Synalinks 21 | # to build directed acyclic graph (DAG) of computation 22 | # from inputs and outputs, like the tensor shape 23 | # in deep learning frameworks. 24 | 25 | qa_pair = Query + Answer 26 | 27 | assert isinstance(qa_pair, synalinks.SymbolicDataModel) 28 | 29 | print(qa_pair.prettify_schema()) 30 | # { 31 | # "additionalProperties": false, 32 | # "properties": { 33 | # "query": { 34 | # "title": "Query", 35 | # "type": "string" 36 | # }, 37 | # "answer": { 38 | # "title": "Answer", 39 | # "type": "string" 40 | # } 41 | # }, 42 | # "required": [ 43 | # "query", 44 | # "answer" 45 | # ], 46 | # "title": "Query", 47 | # "type": "object" 48 | # } 49 | 50 | # Once we concatenate two instanciated data models, the result 51 | # is a JsonDataModel, a data model containing both a JSON schema and 52 | # a JSON object containing the actual data. 53 | 54 | qa_pair = Query(query="What is the French city of aeronautics and robotics?") + Answer( 55 | answer="Toulouse" 56 | ) 57 | 58 | assert isinstance(qa_pair, synalinks.JsonDataModel) 59 | 60 | print(qa_pair.prettify_json()) 61 | # { 62 | # "query": "What is the French city of aeronautics and robotics?", 63 | # "answer": "Toulouse" 64 | # } 65 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | python-version: ["3.10", "3.11", "3.12", "3.13"] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Display Python version 25 | run: python -c "import sys; print(sys.version)" 26 | - name: Install Neo4j tools 27 | run: | 28 | wget -O - https://debian.neo4j.com/neotechnology.gpg.key | sudo apt-key add - 29 | echo 'deb https://debian.neo4j.com stable latest' | sudo tee -a /etc/apt/sources.list.d/neo4j.list;sudo add-apt-repository -y ppa:openjdk-r/ppa; 30 | sudo apt-get install neo4j -y; 31 | - name: Start Neo4J 32 | run: docker run --publish=7474:7474 --publish=7687:7687 --volume=$HOME/neo4j/data:/data --env=NEO4J_AUTH=none neo4j & 33 | - name: Start MemGraph 34 | run: docker run -p 7688:7687 -p 7444:7444 --name memgraph memgraph/memgraph-mage & 35 | - name: Install Project Manager 36 | run: ./shell/uv.sh 37 | - name: Install Synalinks 38 | run: ./shell/install.sh 39 | - name: Install pytest 40 | run: uv pip install pytest pytest-cov 41 | - name: Ping Neo4J 42 | run: cypher-shell -a bolt://localhost:7687 -u neo4j "RETURN 1 as ping;" 43 | - name: Ping MemGraph 44 | run: cypher-shell -a bolt://localhost:7688 -u memgraph "RETURN 1 as ping;" 45 | - name: Run the tests 46 | run: ./shell/test.sh -------------------------------------------------------------------------------- /synalinks/src/rewards/lm_as_judge_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from unittest.mock import patch 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import DataModel 7 | from synalinks.src.backend import Field 8 | from synalinks.src.language_models import LanguageModel 9 | from synalinks.src.rewards.lm_as_judge import LMAsJudge 10 | 11 | 12 | class LMAsJudgeTest(testing.TestCase): 13 | @patch("litellm.acompletion") 14 | async def test_lm_as_judge(self, mock_completion): 15 | class Query(DataModel): 16 | query: str = Field(description="The user query") 17 | 18 | class AnswerWithThinking(DataModel): 19 | thinking: str = Field(description="The step by step thinking process") 20 | answer: str = Field(description="The correct answer") 21 | 22 | language_model = LanguageModel(model="ollama/mistral") 23 | 24 | reward = LMAsJudge(language_model=language_model) 25 | 26 | inputs = Query(query="What is the French capital?") 27 | 28 | y_true = AnswerWithThinking( 29 | thinking="The French capital is Paris", 30 | answer="Paris", 31 | ) 32 | 33 | y_pred = AnswerWithThinking( 34 | thinking="The French capital is well known", 35 | answer="Paris", 36 | ) 37 | 38 | y_pred = inputs + y_pred 39 | 40 | expected_string = ( 41 | """{"critique": "The answer is correct so we can attribute a high reward", """ 42 | """"reward": 1.0}""" 43 | ) 44 | 45 | mock_completion.return_value = { 46 | "choices": [{"message": {"content": expected_string}}] 47 | } 48 | 49 | score = await reward(y_true=y_true, y_pred=y_pred) 50 | self.assertEqual(score, 1.0) 51 | -------------------------------------------------------------------------------- /synalinks/src/modules/masking/in_mask_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules import Input 6 | from synalinks.src.modules.masking.in_mask import InMask 7 | from synalinks.src.programs import Program 8 | 9 | 10 | class Document(DataModel): 11 | title: str 12 | text: str 13 | 14 | 15 | class InMaskTest(testing.TestCase): 16 | async def test_in_mask_single_data_model(self): 17 | inputs = Input(data_model=Document) 18 | 19 | outputs = await InMask( 20 | mask=["text"], 21 | )(inputs) 22 | 23 | program = Program( 24 | inputs=inputs, 25 | outputs=outputs, 26 | name="masking_program", 27 | description="A program to keep fields", 28 | ) 29 | 30 | doc = Document(title="Test document", text="Hello world") 31 | 32 | result = await program(doc) 33 | 34 | self.assertTrue(len(result.keys()) == 1) 35 | 36 | async def test_in_mask_multiple_data_models(self): 37 | inputs = [Input(data_model=Document), Input(data_model=Document)] 38 | 39 | outputs = await InMask( 40 | mask=["text"], 41 | )(inputs) 42 | 43 | program = Program( 44 | inputs=inputs, 45 | outputs=outputs, 46 | name="masking_program", 47 | description="A program to keep fields", 48 | ) 49 | 50 | doc1 = Document(title="Test document 1", text="Hello world") 51 | doc2 = Document(title="Test document 2", text="Hello world") 52 | 53 | results = await program([doc1, doc2]) 54 | 55 | self.assertTrue(len(results[0].keys()) == 1) 56 | self.assertTrue(len(results[1].keys()) == 1) 57 | -------------------------------------------------------------------------------- /synalinks/src/modules/masking/out_mask_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules import Input 6 | from synalinks.src.modules.masking.out_mask import OutMask 7 | from synalinks.src.programs import Program 8 | 9 | 10 | class Document(DataModel): 11 | title: str 12 | text: str 13 | 14 | 15 | class InMaskTest(testing.TestCase): 16 | async def test_in_mask_single_data_model(self): 17 | inputs = Input(data_model=Document) 18 | 19 | outputs = await OutMask( 20 | mask=["title"], 21 | )(inputs) 22 | 23 | program = Program( 24 | inputs=inputs, 25 | outputs=outputs, 26 | name="masking_program", 27 | description="A program to remove fields", 28 | ) 29 | 30 | doc = Document(title="Test document", text="Hello world") 31 | 32 | result = await program(doc) 33 | 34 | self.assertTrue(len(result.keys()) == 1) 35 | 36 | async def test_in_mask_multiple_data_models(self): 37 | inputs = [Input(data_model=Document), Input(data_model=Document)] 38 | 39 | outputs = await OutMask( 40 | mask=["title"], 41 | )(inputs) 42 | 43 | program = Program( 44 | inputs=inputs, 45 | outputs=outputs, 46 | name="masking_program", 47 | description="A program to remove fields", 48 | ) 49 | 50 | doc1 = Document(title="Test document 1", text="Hello world") 51 | doc2 = Document(title="Test document 2", text="Hello world") 52 | 53 | results = await program([doc1, doc2]) 54 | 55 | self.assertTrue(len(results[0].keys()) == 1) 56 | self.assertTrue(len(results[1].keys()) == 1) 57 | -------------------------------------------------------------------------------- /synalinks/src/trainers/data_adapters/array_data_adapter_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/trainers/data_adapters/array_data_adapter_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from absl.testing import parameterized 6 | 7 | from synalinks.src import testing 8 | from synalinks.src.testing.test_utils import AnswerWithRationale 9 | from synalinks.src.testing.test_utils import Query 10 | from synalinks.src.testing.test_utils import load_test_data 11 | from synalinks.src.testing.test_utils import named_product 12 | from synalinks.src.trainers.data_adapters.array_data_adapter import ArrayDataAdapter 13 | 14 | 15 | class TestArrayDataAdapter(testing.TestCase): 16 | @parameterized.named_parameters( 17 | named_product( 18 | shuffle=[False, "batch", True], 19 | ) 20 | ) 21 | def test_basic_flow(self, shuffle): 22 | (x, y), (x_test, y_test) = load_test_data() 23 | 24 | adapter = ArrayDataAdapter( 25 | x, 26 | y=y, 27 | batch_size=32, 28 | steps=None, 29 | shuffle=shuffle, 30 | ) 31 | self.assertEqual(adapter.num_batches, 1) 32 | self.assertEqual(adapter.batch_size, 32) 33 | self.assertEqual(adapter.has_partial_batch, True) 34 | self.assertEqual(adapter.partial_batch_size, 15) 35 | 36 | it = adapter.get_numpy_iterator() 37 | for i, batch in enumerate(it): 38 | self.assertEqual(len(batch), 2) 39 | x, y = batch 40 | self.assertIsInstance(batch, tuple) 41 | self.assertIsInstance(x[0], Query) 42 | self.assertIsInstance(x[1], Query) 43 | self.assertIsInstance(y[0], AnswerWithRationale) 44 | self.assertIsInstance(y[1], AnswerWithRationale) 45 | -------------------------------------------------------------------------------- /synalinks/src/trainers/data_adapters/data_adapter_utils.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/trainers/data_adapters/data_adapter_utils.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src.api_export import synalinks_export 6 | 7 | NUM_BATCHES_FOR_SPEC = 2 8 | 9 | 10 | @synalinks_export("synalinks.utils.unpack_x_y") 11 | def unpack_x_y(data): 12 | """Unpacks user-provided data tuple. 13 | 14 | This is a convenience utility to be used when overriding 15 | `Program.train_step`, `Program.test_step`, or `Program.predict_step`. 16 | This utility makes it easy to support data of the form `(x,)`, or 17 | `(x, y)`. 18 | 19 | Args: 20 | data: A tuple of the form `(x,)`, or `(x, y)`. 21 | 22 | Returns: 23 | The unpacked tuple, with `None`s for `y` if they are 24 | not provided. 25 | """ 26 | if isinstance(data, list): 27 | data = tuple(data) 28 | if not isinstance(data, tuple): 29 | return (data, None) 30 | elif len(data) == 1: 31 | return (data[0], None) 32 | elif len(data) == 2: 33 | return (data[0], data[1]) 34 | error_msg = ( 35 | f"Data is expected to be in format `x`, `(x,)`, or `(x, y)`, found: {data}" 36 | ) 37 | raise ValueError(error_msg) 38 | 39 | 40 | @synalinks_export("synalinks.utils.pack_x_y") 41 | def pack_x_y(x, y=None): 42 | """Packs user-provided data into a tuple. 43 | 44 | This is a convenience utility for packing data into the tuple formats 45 | that `Program.fit()` uses. 46 | 47 | Args: 48 | x: `DataModel`s to pass to `Program`. 49 | y: Ground-truth targets to pass to `Program`. 50 | 51 | Returns: 52 | Tuple in the format used in `Program.fit()`. 53 | """ 54 | if y is None: 55 | return (x,) 56 | return (x, y) 57 | -------------------------------------------------------------------------------- /examples/json_operators/concatenate_part_2.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | 3 | synalinks.enable_logging() 4 | 5 | 6 | class Query(synalinks.DataModel): 7 | query: str 8 | 9 | 10 | # Concatenation with Synalinks (Part 2) 11 | 12 | # What happen if you concatenate two data 13 | # models with the same fields? 14 | # When property names conflict, numerical suffixes are 15 | # added to ensure uniqueness. 16 | 17 | two_queries = Query + Query 18 | 19 | print(two_queries.prettify_schema()) 20 | 21 | # { 22 | # "additionalProperties": false, 23 | # "properties": { 24 | # "query": { 25 | # "title": "Query", 26 | # "type": "string" 27 | # }, 28 | # "query_1": { 29 | # "title": "Query 1", 30 | # "type": "string" 31 | # } 32 | # }, 33 | # "required": [ 34 | # "query", 35 | # "query_1" 36 | # ], 37 | # "title": "Query", 38 | # "type": "object" 39 | # } 40 | 41 | two_queries = Query( 42 | query="Why is neuro-symbolic systems powering the next AI wave?" 43 | ) + Query(query="Can you give a multiple of 5?") 44 | 45 | 46 | print(two_queries.prettify_json()) 47 | # { 48 | # "query": "Why is neuro-symbolic systems powering the next AI wave?", 49 | # "query_1": "Can you give a multiple of 5?" 50 | # } 51 | 52 | # Now, what happen when you concatenate with `None`? 53 | # An exception is raised! 54 | 55 | failing_query = Query(query="Why is neuro-symbolic AI powering the next wave?") + None 56 | # ValueError: Received x1=query='Why is neuro-symbolic AI powering the next wave?' and x2=None 57 | 58 | # This behavior can be summarized with the following truth table: 59 | 60 | # Truth Table: 61 | 62 | # | `x1` | `x2` | Concat (`+`) | 63 | # | ------ | ------ | ----------------- | 64 | # | `x1` | `x2` | `x1 + x2` | 65 | # | `x1` | `None` | `Exception` | 66 | # | `None` | `x2` | `Exception` | 67 | # | `None` | `None` | `Exception` | 68 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_and_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules.core.input_module import Input 6 | from synalinks.src.modules.merging.logical_and import And 7 | from synalinks.src.programs.program import Program 8 | 9 | 10 | class LogicalAndTest(testing.TestCase): 11 | async def test_logical_and_module_not_none(self): 12 | class Query(DataModel): 13 | query: str 14 | 15 | i0 = Input(data_model=Query) 16 | i1 = Input(data_model=Query) 17 | i2 = Input(data_model=Query) 18 | output = await And()([i0, i1, i2]) 19 | 20 | program = Program( 21 | inputs=[i0, i1, i2], 22 | outputs=output, 23 | ) 24 | 25 | result = await program( 26 | [ 27 | Query(query="a"), 28 | Query(query="b"), 29 | Query(query="c"), 30 | ] 31 | ) 32 | 33 | expected_json = { 34 | "query": "a", 35 | "query_1": "b", 36 | "query_2": "c", 37 | } 38 | 39 | self.assertEqual(result.get_json(), expected_json) 40 | 41 | async def test_logical_and_module_none(self): 42 | class Query(DataModel): 43 | query: str 44 | 45 | i0 = Input(data_model=Query) 46 | i1 = Input(data_model=Query) 47 | i2 = Input(data_model=Query) 48 | output = await And()([i0, i1, i2]) 49 | 50 | program = Program( 51 | inputs=[i0, i1, i2], 52 | outputs=output, 53 | ) 54 | 55 | result = await program( 56 | [ 57 | None, 58 | Query(query="b"), 59 | Query(query="c"), 60 | ] 61 | ) 62 | 63 | self.assertEqual(result, None) 64 | -------------------------------------------------------------------------------- /synalinks/src/rewards/cosine_similarity_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from unittest.mock import patch 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import DataModel 7 | from synalinks.src.embedding_models import EmbeddingModel 8 | from synalinks.src.rewards.cosine_similarity import CosineSimilarity 9 | 10 | 11 | class CosineSimilarityTest(testing.TestCase): 12 | @patch("litellm.aembedding") 13 | async def test_base_function(self, mock_embedding): 14 | embedding_model = EmbeddingModel(model="ollama/all-minilm") 15 | expected_value = [0.0, 0.1, 0.2, 0.3, 0.4] 16 | mock_embedding.return_value = {"data": [{"embedding": expected_value}]} 17 | 18 | class Answer(DataModel): 19 | answer: str 20 | 21 | y_true = Answer(answer="Paris") 22 | y_pred = Answer(answer="Paris") 23 | 24 | cosine_similarity = CosineSimilarity(embedding_model=embedding_model) 25 | reward = await cosine_similarity(y_true, y_pred) 26 | self.assertEqual(reward, 1.0) 27 | 28 | @patch("litellm.aembedding") 29 | async def test_multiple_fields(self, mock_embedding): 30 | embedding_model = EmbeddingModel(model="ollama/all-minilm") 31 | expected_value = [0.0, 0.1, 0.2, 0.3, 0.4] 32 | mock_embedding.return_value = { 33 | "data": [{"embedding": expected_value}, {"embedding": expected_value}] 34 | } 35 | 36 | class Answer(DataModel): 37 | thinking: str 38 | answer: str 39 | 40 | y_true = Answer(thinking="The capital of France is paris", answer="Paris") 41 | y_pred = Answer(thinking="The capital of France is paris", answer="Paris") 42 | 43 | cosine_similarity = CosineSimilarity(embedding_model=embedding_model) 44 | reward = await cosine_similarity(y_true, y_pred) 45 | self.assertEqual(reward, 1.0) 46 | -------------------------------------------------------------------------------- /examples/knowledge/extraction/knowledge_dataset.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import os 3 | import glob 4 | import numpy as np 5 | 6 | DATASET_FOLDER = "examples/datasets/knowledge_graph" 7 | 8 | 9 | class Document(synalinks.DataModel): 10 | filename: str = synalinks.Field( 11 | description="The document's filename", 12 | ) 13 | content: str = synalinks.Field( 14 | description="The document's content", 15 | ) 16 | 17 | 18 | def load_data(folder: str = DATASET_FOLDER): 19 | dataset = [] 20 | 21 | # Check if folder exists 22 | if not os.path.exists(folder): 23 | print(f"Warning: Dataset folder '{folder}' does not exist") 24 | return np.array(dataset, dtype="object") 25 | 26 | # Define file patterns to search for 27 | file_patterns = ["*.md", "*.txt"] 28 | 29 | # Iterate through each pattern and find matching files 30 | for pattern in file_patterns: 31 | # Use glob to find all files matching the pattern (only in the specified folder) 32 | file_path_pattern = os.path.join(folder, pattern) 33 | matching_files = glob.glob(file_path_pattern) 34 | 35 | # Process each matching file 36 | for file_path in matching_files: 37 | try: 38 | # Read file content 39 | with open(file_path, "r", encoding="utf-8") as file: 40 | content = file.read() 41 | 42 | # Extract filename from path 43 | filename = os.path.basename(file_path) 44 | 45 | # Create Document object 46 | document = Document(filename=filename, content=content) 47 | 48 | dataset.append(document) 49 | 50 | except Exception as e: 51 | print(f"Error reading file {file_path}: {e}") 52 | continue 53 | 54 | print(f"Loaded {len(dataset)} documents from {folder}") 55 | return np.array(dataset, dtype="object") 56 | -------------------------------------------------------------------------------- /docs/Synalinks API/Data Models API/index.md: -------------------------------------------------------------------------------- 1 | # Data Models API 2 | 3 | Synalinks features four distinct types of data models, each serving a unique purpose within the framework: 4 | 5 | - **[DataModel](The DataModel class.md)**: This is the backend-dependent data model, built on Pydantic's `BaseModel`. It is the primary model most users will interact with. It allows for schema and variable declarations, and is used to format datasets. When entering a workflow, this data model is automatically converted into a backend-independent format. 6 | 7 | - **[JsonDataModel](The JsonDataModel class.md)**: This is the backend-independent data model that flows through the pipelines. It holds both a JSON schema and a JSON value, enabling it to perform computations. Unlike the backend-dependent model, this one is dynamically created and modified. 8 | 9 | - **[SymbolicDataModel](The SymbolicDataModel class.md)**: This is the symbolic data model used during the functional API declaration to infer the pipeline's edges and nodes. It only holds a JSON schema, allowing the system to compute the pipeline from inputs and outputs without performing actual computations. 10 | 11 | - **[Variable](The Variable class.md)**: This data model holds the module's state and can be updated during training. It includes a JSON schema and JSON value, enabling computations, and also contains metadata about training. `Optimizer`s can update it during the training process. 12 | 13 | ## Data Models API Overview 14 | 15 | - [The DataModel Class](The DataModel class.md): The backend-dependent data models. 16 | - [The JsonDataModel Class](The JsonDataModel class.md): The backend-independent data models. 17 | - [The SymbolicDataModel Class](The SymbolicDataModel class.md): The symbolic data models. 18 | - [The Variable Class](The Variable class.md): The variable data models that the optimizers can act upon. 19 | - [The Base DataModels](The Base DataModels.md): A collection of basic backend-dependent data models. -------------------------------------------------------------------------------- /synalinks/src/modules/merging/concat_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules.core.input_module import Input 6 | from synalinks.src.modules.merging.concat import Concat 7 | from synalinks.src.programs.program import Program 8 | 9 | 10 | class ConcatTest(testing.TestCase): 11 | async def test_concat_module_not_none(self): 12 | class Query(DataModel): 13 | query: str 14 | 15 | i0 = Input(data_model=Query) 16 | i1 = Input(data_model=Query) 17 | i2 = Input(data_model=Query) 18 | output = await Concat()([i0, i1, i2]) 19 | 20 | program = Program( 21 | inputs=[i0, i1, i2], 22 | outputs=output, 23 | ) 24 | 25 | result = await program( 26 | [ 27 | Query(query="a"), 28 | Query(query="b"), 29 | Query(query="c"), 30 | ] 31 | ) 32 | 33 | expected_json = { 34 | "query": "a", 35 | "query_1": "b", 36 | "query_2": "c", 37 | } 38 | 39 | self.assertEqual(result.get_json(), expected_json) 40 | 41 | async def test_concat_module_none(self): 42 | class Query(DataModel): 43 | query: str 44 | 45 | i0 = Input(data_model=Query) 46 | i1 = Input(data_model=Query) 47 | i2 = Input(data_model=Query) 48 | output = await Concat()([i0, i1, i2]) 49 | 50 | program = Program( 51 | inputs=[i0, i1, i2], 52 | outputs=output, 53 | ) 54 | 55 | with self.assertRaisesRegex(ValueError, "Received x1=None"): 56 | _ = await program( 57 | [ 58 | None, 59 | Query(query="b"), 60 | Query(query="c"), 61 | ] 62 | ) 63 | -------------------------------------------------------------------------------- /synalinks/src/rewards/exact_match_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.rewards.exact_match import ExactMatch 6 | 7 | 8 | class ExactMatchTest(testing.TestCase): 9 | async def test_base_function(self): 10 | class Answer(DataModel): 11 | answer: str 12 | 13 | y_true = Answer(answer="Paris") 14 | y_pred = Answer(answer="Toulouse") 15 | exact_match = ExactMatch() 16 | reward = await exact_match(y_true, y_pred) 17 | self.assertEqual(reward, 0.0) 18 | 19 | y_true = Answer(answer="Paris") 20 | y_pred = Answer(answer="Paris") 21 | exact_match = ExactMatch() 22 | reward = await exact_match(y_true, y_pred) 23 | self.assertEqual(reward, 1.0) 24 | 25 | async def test_base_function_masked(self): 26 | class Answer(DataModel): 27 | answer: str 28 | 29 | class AnswerWithText(DataModel): 30 | text: str 31 | answer: str 32 | 33 | y_true = Answer(answer="Paris") 34 | y_pred = AnswerWithText(text="The french capital is Toulouse", answer="Toulouse") 35 | exact_match = ExactMatch(in_mask=["answer"]) 36 | reward = await exact_match(y_true, y_pred) 37 | self.assertEqual(reward, 0.0) 38 | 39 | exact_match = ExactMatch(out_mask=["text"]) 40 | reward = await exact_match(y_true, y_pred) 41 | self.assertEqual(reward, 0.0) 42 | 43 | y_true = Answer(answer="Paris") 44 | y_pred = AnswerWithText(text="The french capital is Paris", answer="Paris") 45 | exact_match = ExactMatch(in_mask=["answer"]) 46 | reward = await exact_match(y_true, y_pred) 47 | self.assertEqual(reward, 1.0) 48 | 49 | exact_match = ExactMatch(out_mask=["text"]) 50 | reward = await exact_match(y_true, y_pred) 51 | self.assertEqual(reward, 1.0) 52 | -------------------------------------------------------------------------------- /synalinks/api/config/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.backend.config import api_base as api_base 8 | from synalinks.src.backend.config import api_key as api_key 9 | from synalinks.src.backend.config import backend as backend 10 | from synalinks.src.backend.config import disable_telemetry as disable_telemetry 11 | from synalinks.src.backend.config import enable_logging as enable_logging 12 | from synalinks.src.backend.config import enable_observability as enable_observability 13 | from synalinks.src.backend.config import epsilon as epsilon 14 | from synalinks.src.backend.config import floatx as floatx 15 | from synalinks.src.backend.config import get_seed as get_seed 16 | from synalinks.src.backend.config import ( 17 | is_observability_enabled as is_observability_enabled, 18 | ) 19 | from synalinks.src.backend.config import is_telemetry_enabled as is_telemetry_enabled 20 | from synalinks.src.backend.config import set_api_base as set_api_base 21 | from synalinks.src.backend.config import set_api_key as set_api_key 22 | from synalinks.src.backend.config import set_backend as set_backend 23 | from synalinks.src.backend.config import set_epsilon as set_epsilon 24 | from synalinks.src.backend.config import set_floatx as set_floatx 25 | from synalinks.src.backend.config import set_seed as set_seed 26 | from synalinks.src.backend.config import synalinks_home as synalinks_home 27 | from synalinks.src.saving.serialization_lib import ( 28 | enable_unsafe_deserialization as enable_unsafe_deserialization, 29 | ) 30 | from synalinks.src.utils.io_utils import ( 31 | disable_interactive_logging as disable_interactive_logging, 32 | ) 33 | from synalinks.src.utils.io_utils import ( 34 | enable_interactive_logging as enable_interactive_logging, 35 | ) 36 | from synalinks.src.utils.io_utils import ( 37 | is_interactive_logging_enabled as is_interactive_logging_enabled, 38 | ) 39 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_or_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules.core.input_module import Input 6 | from synalinks.src.modules.merging.logical_or import Or 7 | from synalinks.src.programs.program import Program 8 | 9 | 10 | class LogicalOrTest(testing.TestCase): 11 | async def test_logical_or_module_not_none(self): 12 | class Query(DataModel): 13 | query: str 14 | 15 | i0 = Input(data_model=Query) 16 | i1 = Input(data_model=Query) 17 | i2 = Input(data_model=Query) 18 | output = await Or()([i0, i1, i2]) 19 | 20 | program = Program( 21 | inputs=[i0, i1, i2], 22 | outputs=output, 23 | ) 24 | 25 | result = await program( 26 | [ 27 | Query(query="a"), 28 | Query(query="b"), 29 | Query(query="c"), 30 | ] 31 | ) 32 | 33 | expected_json = { 34 | "query": "a", 35 | "query_1": "b", 36 | "query_2": "c", 37 | } 38 | 39 | self.assertEqual(result.get_json(), expected_json) 40 | 41 | async def test_logical_or_module_none(self): 42 | class Query(DataModel): 43 | query: str 44 | 45 | i0 = Input(data_model=Query) 46 | i1 = Input(data_model=Query) 47 | i2 = Input(data_model=Query) 48 | output = await Or()([i0, i1, i2]) 49 | 50 | program = Program( 51 | inputs=[i0, i1, i2], 52 | outputs=output, 53 | ) 54 | 55 | result = await program( 56 | [ 57 | None, 58 | Query(query="b"), 59 | None, 60 | ] 61 | ) 62 | 63 | expected_json = { 64 | "query": "b", 65 | } 66 | 67 | self.assertEqual(result.get_json(), expected_json) 68 | -------------------------------------------------------------------------------- /docs/Synalinks API/Programs API/index.md: -------------------------------------------------------------------------------- 1 | # Programs API 2 | 3 | Synalinks offers 4 methods to create programs, each tailored to different levels of complexity and use cases: 4 | 5 | - **[Sequential Program](The%20Sequential%20class.md)**: This is the simplest method, involving a straightforward list of modules. Ideal for single-input, single-output stacks of modules. However, it is limited in flexibility compared to other methods. 6 | 7 | - **[Functional Program](The%20Program%20class.md)**: This is a fully-featured API that supports arbitrary program architectures. Easy to use and suitable for most users, offering greater flexibility than the Sequential program. 8 | 9 | - **[Mixing Subclassing and Functional API](The%20Program%20class.md)**: This make easier to encapsulate your programs made with the functional API into bigger systems. 10 | 11 | - **[Program Subclassing](The%20Program%20class.md)**: This method allows you to implement everything from scratch. Ideal for complex or research use cases. It is also the preferred method for contributing. 12 | 13 | ## Programs API Overview 14 | 15 | ### The Program class 16 | 17 | - [Program class](The Program class.md) 18 | - [summary method](The Program class.md) 19 | - [get_module method](The Program class.md) 20 | 21 | ### The Sequential class 22 | 23 | - [Sequential class](The Sequential class.md) 24 | - [add method](The Sequential class.md) 25 | - [pop method](The Sequential class.md) 26 | 27 | ### Program training APIs 28 | 29 | - [compile method](Program training API.md) 30 | - [fit method](Program training API.md) 31 | - [evaluate method](Program training API.md) 32 | - [predict method](Program training API.md) 33 | - [train_on_batch method](Program training API.md) 34 | - [test_on_batch method](Program training API.md) 35 | - [predict_on_batch method](Program training API.md) 36 | 37 | ### Saving & Serialization 38 | 39 | - [Whole program saving and loading](Program Saving API/Program saving and loading.md) 40 | - [Variables-only saving and loading](Program Saving API/Variable saving and loading.md) -------------------------------------------------------------------------------- /synalinks/src/utils/mcp/test_common.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import multiprocessing 3 | import socket 4 | import time 5 | from collections.abc import Generator 6 | 7 | import uvicorn 8 | from mcp.server.fastmcp import FastMCP 9 | 10 | 11 | def run_streamable_server(server: FastMCP, server_port: int) -> None: 12 | """Run a FastMCP server in a separate process exposing a streamable HTTP endpoint.""" 13 | app = server.streamable_http_app() 14 | uvicorn_server = uvicorn.Server( 15 | config=uvicorn.Config( 16 | app=app, host="127.0.0.1", port=server_port, log_level="error" 17 | ) 18 | ) 19 | uvicorn_server.run() 20 | 21 | 22 | @contextlib.contextmanager 23 | def run_streamable_server_multiprocessing(server: FastMCP) -> Generator[None, None, None]: 24 | """Run the server in a separate process exposing a streamable HTTP endpoint. 25 | 26 | The endpoint will be available at `http://localhost:{server.settings.port}/mcp/`. 27 | """ 28 | proc = multiprocessing.Process( 29 | target=run_streamable_server, 30 | kwargs={"server": server, "server_port": server.settings.port}, 31 | daemon=True, 32 | ) 33 | proc.start() 34 | 35 | # Wait for server to be running 36 | max_attempts = 20 37 | attempt = 0 38 | 39 | while attempt < max_attempts: 40 | try: 41 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 42 | s.connect(("127.0.0.1", server.settings.port)) 43 | break 44 | except ConnectionRefusedError: 45 | time.sleep(0.1) 46 | attempt += 1 47 | else: 48 | raise RuntimeError(f"Server failed to start after {max_attempts} attempts") 49 | 50 | try: 51 | yield 52 | finally: 53 | # Signal the server to stop 54 | proc.kill() 55 | proc.join(timeout=2) 56 | if proc.is_alive(): 57 | raise RuntimeError( 58 | "Server process is still alive after attempting to terminate it" 59 | ) 60 | -------------------------------------------------------------------------------- /synalinks/src/ops/symbolic_arguments.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/ops/symbolic_arguments.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src import tree 6 | from synalinks.src.backend import SymbolicDataModel 7 | 8 | 9 | class SymbolicArguments: 10 | def __init__(self, *args, **kwargs): 11 | self.args = tree.map_structure(lambda x: x, args) 12 | self.kwargs = tree.map_structure(lambda x: x, kwargs) 13 | self._flat_arguments = tree.flatten((self.args, self.kwargs)) 14 | 15 | # Used to avoid expensive `tree` operations in the most common case. 16 | if ( 17 | not self.kwargs 18 | and len(self.args) == 1 19 | and isinstance(self.args[0], SymbolicDataModel) 20 | ): 21 | self._single_positional_data_model = self.args[0] 22 | else: 23 | self._single_positional_data_model = None 24 | 25 | self.symbolic_data_models = [] 26 | for arg in self._flat_arguments: 27 | if isinstance(arg, SymbolicDataModel): 28 | self.symbolic_data_models.append(arg) 29 | 30 | def convert(self, conversion_fn): 31 | args = tree.map_structure(conversion_fn, self.args) 32 | kwargs = tree.map_structure(conversion_fn, self.kwargs) 33 | return args, kwargs 34 | 35 | def fill_in(self, data_model_dict): 36 | """Maps SymbolicDataModels to computed values using `data_model_dict`. 37 | 38 | `data_model_dict` maps `SymbolicDataModel` instances to their current values. 39 | """ 40 | if self._single_positional_data_model is not None: 41 | # Performance optimization for most common case. 42 | # Approx. 70x faster. 43 | return (data_model_dict[id(self._single_positional_data_model)],), {} 44 | 45 | def switch_fn(x): 46 | if isinstance(x, SymbolicDataModel): 47 | return data_model_dict.get(id(x), None) 48 | return x 49 | 50 | return self.convert(switch_fn) 51 | -------------------------------------------------------------------------------- /synalinks/api/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """DO NOT EDIT. 2 | 3 | This file was autogenerated. Do not edit it by hand, 4 | since your modifications would be overwritten. 5 | """ 6 | 7 | from synalinks.src.modules import deserialize as deserialize 8 | from synalinks.src.modules import serialize as serialize 9 | from synalinks.src.modules.agents.function_calling_agent import ( 10 | FunctionCallingAgent as FunctionCallingAgent, 11 | ) 12 | from synalinks.src.modules.core.action import Action as Action 13 | from synalinks.src.modules.core.branch import Branch as Branch 14 | from synalinks.src.modules.core.decision import Decision as Decision 15 | from synalinks.src.modules.core.generator import Generator as Generator 16 | from synalinks.src.modules.core.identity import Identity as Identity 17 | from synalinks.src.modules.core.input_module import Input as Input 18 | from synalinks.src.modules.core.input_module import InputModule as InputModule 19 | from synalinks.src.modules.core.not_module import Not as Not 20 | from synalinks.src.modules.knowledge.embedding import Embedding as Embedding 21 | from synalinks.src.modules.knowledge.update_knowledge import ( 22 | UpdateKnowledge as UpdateKnowledge, 23 | ) 24 | from synalinks.src.modules.masking.in_mask import InMask as InMask 25 | from synalinks.src.modules.masking.out_mask import OutMask as OutMask 26 | from synalinks.src.modules.merging.concat import Concat as Concat 27 | from synalinks.src.modules.merging.concat import Concat as Concatenate 28 | from synalinks.src.modules.merging.logical_and import And as And 29 | from synalinks.src.modules.merging.logical_or import Or as Or 30 | from synalinks.src.modules.merging.logical_xor import Xor as Xor 31 | from synalinks.src.modules.module import Module as Module 32 | from synalinks.src.modules.retrievers.entity_retriever import ( 33 | EntityRetriever as EntityRetriever, 34 | ) 35 | from synalinks.src.modules.retrievers.triplet_retriever import ( 36 | TripletRetriever as TripletRetriever, 37 | ) 38 | from synalinks.src.modules.synthesis.python_synthesis import ( 39 | PythonSynthesis as PythonSynthesis, 40 | ) 41 | from synalinks.src.modules.ttc.chain_of_thought import ChainOfThought as ChainOfThought 42 | from synalinks.src.modules.ttc.self_critique import SelfCritique as SelfCritique 43 | -------------------------------------------------------------------------------- /synalinks/src/rewards/exact_match.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src.api_export import synalinks_export 4 | from synalinks.src.rewards.reward_wrappers import RewardFunctionWrapper 5 | 6 | 7 | @synalinks_export("synalinks.rewards.exact_match") 8 | async def exact_match(y_true, y_pred): 9 | """ 10 | Computes the exact match between `y_true` and `y_pred`. 11 | 12 | If their values are equal, it returns a reward of 1.0; otherwise, it returns 0.0. 13 | 14 | Args: 15 | y_true (JsonDataModel): The ground truth JSON data_model. 16 | y_pred (JsonDataModel): The predicted JSON data_model. 17 | 18 | Returns: 19 | (float): The reward value, which is 1.0 if the values match exactly, 20 | and 0.0 otherwise. 21 | """ 22 | reward = 0.0 23 | if y_pred is not None: 24 | if y_pred.get_json() == y_true.get_json(): 25 | reward = 1.0 26 | return reward 27 | 28 | 29 | @synalinks_export( 30 | [ 31 | "synalinks.ExactMatch", 32 | "synalinks.rewards.ExactMatch", 33 | ] 34 | ) 35 | class ExactMatch(RewardFunctionWrapper): 36 | """Computes the exact match between `y_true` and `y_pred`. 37 | 38 | Example: 39 | 40 | ```python 41 | program.compile( 42 | reward=synalinks.rewards.ExactMatch(), 43 | optimizer=synalinks.optimizers.RandomFewShot(), 44 | ) 45 | ``` 46 | 47 | Args: 48 | name (str): Optional. string name of the reward instance. 49 | in_mask (list): Optional. list of keys to keep to compute the reward. 50 | out_mask (list): Optional. list of keys to remove to compute the reward. 51 | """ 52 | 53 | def __init__( 54 | self, 55 | name="exact_match", 56 | in_mask=None, 57 | out_mask=None, 58 | ): 59 | super().__init__( 60 | fn=exact_match, 61 | name=name, 62 | in_mask=in_mask, 63 | out_mask=out_mask, 64 | ) 65 | 66 | def get_config(self): 67 | return { 68 | "name": self.name, 69 | "in_mask": self.in_mask, 70 | "out_mask": self.out_mask, 71 | } 72 | 73 | @classmethod 74 | def from_config(cls, config): 75 | return cls(**config) 76 | -------------------------------------------------------------------------------- /synalinks/src/backend/common/global_state.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/backend/common/global_state.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import gc 6 | import threading 7 | 8 | from synalinks.src.api_export import synalinks_export 9 | 10 | GLOBAL_STATE_TRACKER = threading.local() 11 | GLOBAL_SETTINGS_TRACKER = threading.local() 12 | 13 | 14 | def set_global_attribute(name, value): 15 | setattr(GLOBAL_STATE_TRACKER, name, value) 16 | 17 | 18 | def get_global_attribute(name, default=None, set_to_default=False): 19 | attr = getattr(GLOBAL_STATE_TRACKER, name, None) 20 | if attr is None and default is not None: 21 | attr = default 22 | if set_to_default: 23 | set_global_attribute(name, attr) 24 | return attr 25 | 26 | 27 | @synalinks_export( 28 | [ 29 | "synalinks.utils.clear_session", 30 | "synalinks.backend.clear_session", 31 | "synalinks.clear_session", 32 | ] 33 | ) 34 | def clear_session(free_memory=True): 35 | """Resets all state generated by synalinks. 36 | 37 | synalinks manages a global state, which it uses to implement the Functional 38 | model-building API and to uniquify autogenerated modules names. 39 | 40 | If you are creating many models in a loop, this global state will consume 41 | an increasing amount of memory over time, and you may want to clear it. 42 | Calling `clear_session()` releases the global state: this helps avoid 43 | clutter from old programs and modules, especially when memory is limited. 44 | 45 | Args: 46 | free_memory (bool): Whether to call Python garbage collection. 47 | It's usually a good practice to call it to make sure 48 | memory used by deleted objects is immediately freed. 49 | However, it may take a few seconds to execute, so 50 | when using `clear_session()` in a short loop, 51 | you may want to skip it. 52 | """ 53 | global GLOBAL_STATE_TRACKER 54 | global GLOBAL_SETTINGS_TRACKER 55 | 56 | GLOBAL_STATE_TRACKER = threading.local() 57 | GLOBAL_SETTINGS_TRACKER = threading.local() 58 | 59 | if free_memory: 60 | # Manually trigger garbage collection. 61 | gc.collect() 62 | -------------------------------------------------------------------------------- /synalinks/src/optimizers/random_few_shot_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from unittest.mock import patch 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.language_models import LanguageModel 7 | from synalinks.src.modules import Generator 8 | from synalinks.src.modules import Input 9 | from synalinks.src.optimizers.random_few_shot import RandomFewShot 10 | from synalinks.src.programs import Program 11 | from synalinks.src.rewards.exact_match import ExactMatch 12 | from synalinks.src.testing.test_utils import AnswerWithRationale 13 | from synalinks.src.testing.test_utils import Query 14 | from synalinks.src.testing.test_utils import load_test_data 15 | from synalinks.src.testing.test_utils import mock_completion_data 16 | from synalinks.src.testing.test_utils import mock_incorrect_completion_data 17 | 18 | 19 | class RandomFewShotTest(testing.TestCase): 20 | @patch("litellm.acompletion") 21 | async def test_random_few_shot_training(self, mock_completion): 22 | language_model = LanguageModel( 23 | model="ollama/mistral", 24 | ) 25 | 26 | inputs = Input(data_model=Query) 27 | outputs = await Generator( 28 | language_model=language_model, 29 | data_model=AnswerWithRationale, 30 | )(inputs) 31 | 32 | program = Program( 33 | inputs=inputs, 34 | outputs=outputs, 35 | name="test_program", 36 | description="A test program", 37 | ) 38 | 39 | program.compile( 40 | optimizer=RandomFewShot( 41 | nb_min_examples=1, 42 | ), 43 | reward=ExactMatch(in_mask=["answer"]), 44 | ) 45 | 46 | (x_train, y_train), (x_test, y_test) = load_test_data() 47 | 48 | mock_responses = [] 49 | mock_responses.extend(mock_incorrect_completion_data()) 50 | mock_responses.extend(mock_completion_data()) 51 | 52 | mock_completion.side_effect = mock_responses 53 | 54 | _ = await program.fit( 55 | x=x_train, 56 | y=y_train, 57 | epochs=2, 58 | batch_size=32, 59 | ) 60 | 61 | program_vars = program.get_variable(index=0).get_json() 62 | self.assertTrue(len(program_vars["examples"]) > 0) 63 | -------------------------------------------------------------------------------- /synalinks/src/modules/core/identity.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import tree 4 | from synalinks.src.api_export import synalinks_export 5 | from synalinks.src.backend import JsonDataModel 6 | from synalinks.src.backend import SymbolicDataModel 7 | from synalinks.src.modules.module import Module 8 | 9 | 10 | @synalinks_export(["synalinks.modules.Identity", "synalinks.Identity"]) 11 | class Identity(Module): 12 | """Identity module. 13 | 14 | This module should be used as a placeholder when no operation is to be 15 | performed. The module just returns its `inputs` argument as output. 16 | 17 | This module can be really useful during development process in order to 18 | implement the whole program architecture before the individual modules. 19 | 20 | It avoid any data models naming issue that you could have by just 21 | forwarding the inputs, that way you can implement the general 22 | program architecture, validate it and implement the individual 23 | modules later. 24 | 25 | Example: 26 | 27 | ```python 28 | import synalinks 29 | 30 | class MyAwesomeModule(synalinks.Program): 31 | 32 | def __init__( 33 | name=None, 34 | description=None, 35 | trainable=True, 36 | ): 37 | super().__init__( 38 | name=name, 39 | description=description, 40 | trainable=trainable, 41 | ) 42 | 43 | async def build(self, inputs): 44 | outputs = await synalinks.Identity()(inputs) 45 | 46 | super().__init__( 47 | inputs=inputs, 48 | outputs=outputs, 49 | name=self.name, 50 | description=self.description, 51 | trainable=self.trainable, 52 | ) 53 | ``` 54 | 55 | Args: 56 | **kwargs (keyword arguments): The default module's arguments 57 | """ 58 | 59 | def __init__(self, **kwargs): 60 | super().__init__(**kwargs) 61 | self.built = True 62 | 63 | async def call(self, inputs): 64 | if isinstance(inputs, (JsonDataModel, SymbolicDataModel)): 65 | return inputs.clone() 66 | return tree.map_structure( 67 | lambda x: x.clone(), 68 | inputs, 69 | ) 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Celery 10 | celerybeat-schedule 11 | celerybeat.pid 12 | 13 | # Cython debug symbols 14 | cython_debug/ 15 | 16 | # distribution / packaging 17 | .eggs/ 18 | .installed.cfg 19 | .Python 20 | *.egg 21 | *.egg-info/ 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | lib/ 28 | lib64/ 29 | MANIFEST 30 | parts/ 31 | sdist/ 32 | share/python-wheels/ 33 | var/ 34 | wheels/ 35 | 36 | # Django 37 | *.log 38 | db.sqlite3 39 | db.sqlite3-journal 40 | local_settings.py 41 | 42 | # environments 43 | .env 44 | .venv 45 | env.bak/ 46 | env/ 47 | ENV/ 48 | venv.bak/ 49 | venv/ 50 | 51 | # Flask 52 | .webassets-cache 53 | instance/ 54 | 55 | # IDEs 56 | .idea/ 57 | .vscode/ 58 | 59 | # IPython 60 | ipython_config.py 61 | profile_default/ 62 | 63 | # Jupyter 64 | .ipynb_checkpoints 65 | 66 | # logging 67 | pip-delete-this-directory.txt 68 | pip-log.txt 69 | 70 | # macOS system files 71 | .DS_Store 72 | 73 | # mkdocs documentation 74 | /site 75 | 76 | # pdm 77 | # pdm stores project-wide configurations in .pdm.toml, 78 | # but it is recommended to not include it in version control. 79 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 80 | .pdm-build/ 81 | .pdm-python 82 | .pdm.toml 83 | 84 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 85 | __pypackages__/ 86 | 87 | # PyBuilder 88 | .pybuilder/ 89 | target/ 90 | 91 | # PyInstaller 92 | *.manifest 93 | *.spec 94 | 95 | # PyPI configuration file 96 | .pypirc 97 | 98 | # Rope settings 99 | .ropeproject 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Scrapy 105 | .scrapy 106 | 107 | # Sphinx documentation 108 | docs/_build/ 109 | 110 | # Spyder settings 111 | .spyderproject 112 | .spyproject 113 | 114 | # static analysis 115 | .dmypy.json 116 | .mypy_cache/ 117 | .pyre/ 118 | .pytype/ 119 | dmypy.json 120 | 121 | # testing 122 | .cache 123 | .coverage 124 | .coverage.* 125 | .hypothesis/ 126 | .nox/ 127 | .pytest_cache/ 128 | .tox/ 129 | *.cover 130 | *.py,cover 131 | cover/ 132 | coverage.xml 133 | htmlcov/ 134 | nosetests.xml 135 | 136 | # translations 137 | *.mo 138 | *.pot 139 | 140 | */AGENTS.md -------------------------------------------------------------------------------- /synalinks/src/hooks/hook.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import utils 4 | from synalinks.src.api_export import synalinks_export 5 | 6 | 7 | @synalinks_export("synalinks.hooks.Hook") 8 | class Hook: 9 | """Base hook class used to build new hooks. 10 | 11 | Hooks are callback-like objects that can be passed to the module's configuration, they 12 | intercept the `__call__()` method of the modules. 13 | 14 | They can be used to log the inputs/outputs of the modules as well as for 15 | streaming data or for observability. 16 | 17 | Attributes: 18 | params (dict): Hook parameters 19 | (eg. endpoint, log-level ...). 20 | module (Module): Instance of `Module`. 21 | Reference of the module being monitored. 22 | """ 23 | 24 | def __init__(self): 25 | self.params = None 26 | self._module = None 27 | 28 | def set_params(self, params): 29 | self.params = params 30 | 31 | def set_module(self, module): 32 | self._module = module 33 | 34 | @property 35 | def module(self): 36 | return self._module 37 | 38 | @utils.default 39 | def on_call_begin( 40 | self, 41 | call_id, 42 | parent_call_id=None, 43 | inputs=None, 44 | ): 45 | """Called at the beginning of the module execution. 46 | 47 | Args: 48 | call_id (str): The id of the module's call 49 | inputs (SymbolicDataModel | JsonDataModel | DataModel | list | dict | tuple): 50 | The module's inputs. The outputs can be data models or lists, 51 | dicts or tuples of data models. 52 | """ 53 | pass 54 | 55 | @utils.default 56 | def on_call_end( 57 | self, 58 | call_id, 59 | parent_call_id=None, 60 | outputs=None, 61 | exception=None, 62 | ): 63 | """Called at the end of the module execution. 64 | 65 | Args: 66 | call_id (str): The id of the module's call 67 | outputs (SymbolicDataModel | JsonDataModel | DataModel | list | dict | tuple): 68 | The module's outputs. The outputs can be data models or lists, 69 | dicts or tuples of data models. 70 | exception (str): Exception message if any. 71 | """ 72 | pass 73 | -------------------------------------------------------------------------------- /synalinks/src/utils/naming.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/utils/naming.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | import collections 6 | import re 7 | 8 | from synalinks.src.api_export import synalinks_export 9 | from synalinks.src.backend.common import global_state 10 | 11 | 12 | def auto_name(prefix): 13 | prefix = to_snake_case(prefix) 14 | return uniquify(prefix) 15 | 16 | 17 | def uniquify(name): 18 | object_name_uids = global_state.get_global_attribute( 19 | "object_name_uids", 20 | default=collections.defaultdict(int), 21 | set_to_default=True, 22 | ) 23 | if name in object_name_uids: 24 | unique_name = f"{name}_{object_name_uids[name]}" 25 | else: 26 | unique_name = name 27 | object_name_uids[name] += 1 28 | return unique_name 29 | 30 | 31 | def to_snake_case(name): 32 | name = re.sub(r"\W+", "", name) 33 | name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) 34 | name = re.sub("([a-z])([A-Z])", r"\1_\2", name).lower() 35 | return name 36 | 37 | 38 | def to_pkg_name(name): 39 | name = name.replace("-", "_") 40 | name = name.replace(" ", "_") 41 | name = to_snake_case(name) 42 | return name 43 | 44 | 45 | @synalinks_export("synalinks.backend.get_uid") 46 | def get_uid(prefix=""): 47 | """Associates a string prefix with an integer counter. 48 | 49 | Args: 50 | prefix: String prefix to index. 51 | 52 | Returns: 53 | Unique integer ID. 54 | 55 | Example: 56 | 57 | >>> get_uid('action') 58 | 1 59 | >>> get_uid('action') 60 | 2 61 | """ 62 | object_name_uids = global_state.get_global_attribute( 63 | "object_name_uids", 64 | default=collections.defaultdict(int), 65 | set_to_default=True, 66 | ) 67 | object_name_uids[prefix] += 1 68 | return object_name_uids[prefix] 69 | 70 | 71 | def reset_uids(): 72 | global_state.set_global_attribute("object_name_uids", collections.defaultdict(int)) 73 | 74 | 75 | def get_object_name(obj): 76 | if hasattr(obj, "name"): # Most synalinks objects. 77 | return obj.name 78 | elif hasattr(obj, "__name__"): # Function. 79 | return to_snake_case(obj.__name__) 80 | elif hasattr(obj, "__class__"): # Class instance. 81 | return to_snake_case(obj.__class__.__name__) 82 | return to_snake_case(str(obj)) 83 | -------------------------------------------------------------------------------- /synalinks/src/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from synalinks.src.api_export import synalinks_export 2 | from synalinks.src.modules.core.action import Action 3 | from synalinks.src.modules.core.branch import Branch 4 | from synalinks.src.modules.core.decision import Decision 5 | from synalinks.src.modules.core.generator import Generator 6 | from synalinks.src.modules.core.generator import default_instructions 7 | from synalinks.src.modules.core.generator import default_prompt_template 8 | from synalinks.src.modules.core.identity import Identity 9 | from synalinks.src.modules.core.input_module import Input 10 | from synalinks.src.modules.core.input_module import InputModule 11 | from synalinks.src.modules.core.not_module import Not 12 | from synalinks.src.modules.knowledge.embedding import Embedding 13 | from synalinks.src.modules.knowledge.update_knowledge import UpdateKnowledge 14 | from synalinks.src.modules.module import Module 15 | from synalinks.src.modules.ttc.chain_of_thought import ChainOfThought 16 | from synalinks.src.modules.ttc.self_critique import SelfCritique 17 | from synalinks.src.saving import serialization_lib 18 | 19 | 20 | @synalinks_export("synalinks.modules.serialize") 21 | def serialize(module): 22 | """Returns the module configuration as a Python dict. 23 | 24 | Args: 25 | module: A `synalinks.modules.module` instance to serialize. 26 | 27 | Returns: 28 | Python dict which contains the configuration of the module. 29 | """ 30 | return serialization_lib.serialize_synalinks_object(module) 31 | 32 | 33 | @synalinks_export("synalinks.modules.deserialize") 34 | def deserialize(config, custom_objects=None): 35 | """Returns a Synalinks module object via its configuration. 36 | 37 | Args: 38 | config: A python dict containing a serialized module configuration. 39 | custom_objects: Optional dictionary mapping names (strings) to custom 40 | objects (classes and functions) to be considered during 41 | deserialization. 42 | 43 | Returns: 44 | A Synalinks module instance. 45 | """ 46 | obj = serialization_lib.deserialize_synalinks_object( 47 | config, 48 | custom_objects=custom_objects, 49 | ) 50 | if not isinstance(obj, Module): 51 | raise ValueError( 52 | "`synalinks.modules.deserialize` was passed a `config` object that is " 53 | f"not a `synalinks.modules.Module`. Received: {config}" 54 | ) 55 | return obj 56 | -------------------------------------------------------------------------------- /synalinks/src/backend/common/name_scope_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/backend/common/name_scope_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend.common.name_scope import current_path 7 | from synalinks.src.backend.common.name_scope import name_scope 8 | 9 | 10 | class NameScopeTest(testing.TestCase): 11 | def test_stacking(self): 12 | self.assertEqual(current_path(), "") 13 | with name_scope("outer") as outer: 14 | self.assertEqual(outer.name, "outer") 15 | self.assertEqual(current_path(), "outer") 16 | with name_scope("middle") as middle: 17 | self.assertEqual(middle.name, "middle") 18 | self.assertEqual(current_path(), "outer/middle") 19 | with name_scope("inner") as inner: 20 | self.assertEqual(inner.name, "inner") 21 | self.assertEqual(current_path(), "outer/middle/inner") 22 | self.assertEqual(current_path(), "outer/middle") 23 | self.assertEqual(current_path(), "outer") 24 | self.assertEqual(current_path(), "") 25 | 26 | def test_deduplication(self): 27 | self.assertEqual(current_path(), "") 28 | with name_scope("name", caller=1): 29 | with name_scope("name", caller=1): 30 | self.assertEqual(current_path(), "name") 31 | self.assertEqual(current_path(), "") 32 | with name_scope("name"): 33 | with name_scope("name"): 34 | self.assertEqual(current_path(), "name/name") 35 | 36 | def test_errors(self): 37 | with self.assertRaisesRegex(ValueError, "must be a string"): 38 | name_scope("foo/bar") 39 | with self.assertRaisesRegex(ValueError, "must be a string"): 40 | name_scope(4) 41 | 42 | def test_override_parent(self): 43 | self.assertEqual(current_path(), "") 44 | with name_scope("outer"): 45 | self.assertEqual(current_path(), "outer") 46 | with name_scope("middle", override_parent="/absolute/path"): 47 | self.assertEqual(current_path(), "absolute/path/middle") 48 | with name_scope("inner"): 49 | self.assertEqual(current_path(), "absolute/path/middle/inner") 50 | self.assertEqual(current_path(), "outer") 51 | -------------------------------------------------------------------------------- /examples/knowledge/retrieval/retrieval_augmented_generation.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | 4 | from knowledge_graph_schema import City, Country, Place, Event 5 | from knowledge_graph_schema import IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn 6 | 7 | 8 | class Query(synalinks.DataModel): 9 | query: str = synalinks.Field( 10 | description="The user query", 11 | ) 12 | 13 | 14 | class Answer(synalinks.DataModel): 15 | answer: str = synalinks.Field( 16 | description="The answer to the user query", 17 | ) 18 | 19 | 20 | async def main(): 21 | language_model = synalinks.LanguageModel( 22 | model="ollama/mistral", 23 | ) 24 | 25 | embedding_model = synalinks.EmbeddingModel( 26 | model="ollama/mxbai-embed-large", 27 | ) 28 | 29 | knowledge_base = synalinks.KnowledgeBase( 30 | uri="memgraph://localhost:7687", 31 | entity_models=[City, Country, Place, Event], 32 | relation_models=[IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn], 33 | embedding_model=embedding_model, 34 | metric="cosine", 35 | wipe_on_start=False, 36 | ) 37 | 38 | inputs = synalinks.Input(data_model=Query) 39 | query_result = await synalinks.EntityRetriever( 40 | entity_models=[City, Country, Place, Event], 41 | knowledge_base=knowledge_base, 42 | language_model=language_model, 43 | return_inputs=True, 44 | return_query=True, 45 | )(inputs) 46 | outputs = await synalinks.Generator( 47 | data_model=Answer, 48 | language_model=language_model, 49 | instructions=[ 50 | "Your task is to answer in natural language to the query based on the results of the search", 51 | "If the result of the search is not relevant, just say that you don't know", 52 | ], 53 | return_inputs=True, 54 | )(query_result) 55 | 56 | program = synalinks.Program( 57 | inputs=inputs, 58 | outputs=outputs, 59 | name="simple_rag", 60 | description="A simple RAG program", 61 | ) 62 | 63 | synalinks.utils.plot_program( 64 | program, 65 | to_folder="examples/knowledge/retrieval", 66 | show_trainable=True, 67 | show_schemas=True, 68 | ) 69 | 70 | result = await program(Query(query="What is the French capital?")) 71 | 72 | print(result.prettify_json()) 73 | 74 | 75 | if __name__ == "__main__": 76 | asyncio.run(main()) 77 | -------------------------------------------------------------------------------- /examples/knowledge/extraction/knowledge_graph_schema.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | from typing import Literal, Union 3 | 4 | 5 | class City(synalinks.Entity): 6 | label: Literal["City"] 7 | name: str = synalinks.Field( 8 | description="The name of a city, such as 'Paris' or 'New York'.", 9 | ) 10 | 11 | 12 | class Country(synalinks.Entity): 13 | label: Literal["Country"] 14 | name: str = synalinks.Field( 15 | description="The name of a country, such as 'France' or 'Canada'.", 16 | ) 17 | 18 | 19 | class Place(synalinks.Entity): 20 | label: Literal["Place"] 21 | name: str = synalinks.Field( 22 | description="The name of a specific place, which could be a landmark, building, or any point of interest, such as 'Eiffel Tower' or 'Statue of Liberty'.", 23 | ) 24 | 25 | 26 | class Event(synalinks.Entity): 27 | label: Literal["Event"] 28 | name: str = synalinks.Field( 29 | description="The name of an event, such as 'Summer Olympics 2024' or 'Woodstock 1969'.", 30 | ) 31 | 32 | 33 | class IsCapitalOf(synalinks.Relation): 34 | subj: City = synalinks.Field( 35 | description="The city entity that serves as the capital.", 36 | ) 37 | label: Literal["IsCapitalOf"] 38 | obj: Country = synalinks.Field( 39 | description="The country entity for which the city is the capital.", 40 | ) 41 | 42 | 43 | class IsCityOf(synalinks.Relation): 44 | subj: City = synalinks.Field( 45 | description="The city entity that is a constituent part of a country.", 46 | ) 47 | label: Literal["IsCityOf"] 48 | obj: Country = synalinks.Field( 49 | description="The country entity that the city is part of.", 50 | ) 51 | 52 | 53 | class IsLocatedIn(synalinks.Relation): 54 | subj: Union[Place] = synalinks.Field( 55 | description="The place entity that is situated within a larger geographical area.", 56 | ) 57 | label: Literal["IsLocatedIn"] 58 | obj: Union[City, Country] = synalinks.Field( 59 | description="The city or country entity where the place is geographically located.", 60 | ) 61 | 62 | 63 | class TookPlaceIn(synalinks.Relation): 64 | subj: Event = synalinks.Field( 65 | description="The event entity that occurred in a specific location.", 66 | ) 67 | label: Literal["TookPlaceIn"] 68 | obj: Union[City, Country] = synalinks.Field( 69 | description="The city or country entity where the event occurred.", 70 | ) 71 | -------------------------------------------------------------------------------- /examples/knowledge/retrieval/knowledge_graph_schema.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | from typing import Literal, Union 3 | 4 | 5 | class City(synalinks.Entity): 6 | label: Literal["City"] 7 | name: str = synalinks.Field( 8 | description="The name of a city, such as 'Paris' or 'New York'.", 9 | ) 10 | 11 | 12 | class Country(synalinks.Entity): 13 | label: Literal["Country"] 14 | name: str = synalinks.Field( 15 | description="The name of a country, such as 'France' or 'Canada'.", 16 | ) 17 | 18 | 19 | class Place(synalinks.Entity): 20 | label: Literal["Place"] 21 | name: str = synalinks.Field( 22 | description="The name of a specific place, which could be a landmark, building, or any point of interest, such as 'Eiffel Tower' or 'Statue of Liberty'.", 23 | ) 24 | 25 | 26 | class Event(synalinks.Entity): 27 | label: Literal["Event"] 28 | name: str = synalinks.Field( 29 | description="The name of an event, such as 'Summer Olympics 2024' or 'Woodstock 1969'.", 30 | ) 31 | 32 | 33 | class IsCapitalOf(synalinks.Relation): 34 | subj: City = synalinks.Field( 35 | description="The city entity that serves as the capital.", 36 | ) 37 | label: Literal["IsCapitalOf"] 38 | obj: Country = synalinks.Field( 39 | description="The country entity for which the city is the capital.", 40 | ) 41 | 42 | 43 | class IsCityOf(synalinks.Relation): 44 | subj: City = synalinks.Field( 45 | description="The city entity that is a constituent part of a country.", 46 | ) 47 | label: Literal["IsCityOf"] 48 | obj: Country = synalinks.Field( 49 | description="The country entity that the city is part of.", 50 | ) 51 | 52 | 53 | class IsLocatedIn(synalinks.Relation): 54 | subj: Union[Place] = synalinks.Field( 55 | description="The place entity that is situated within a larger geographical area.", 56 | ) 57 | label: Literal["IsLocatedIn"] 58 | obj: Union[City, Country] = synalinks.Field( 59 | description="The city or country entity where the place is geographically located.", 60 | ) 61 | 62 | 63 | class TookPlaceIn(synalinks.Relation): 64 | subj: Event = synalinks.Field( 65 | description="The event entity that occurred in a specific location.", 66 | ) 67 | label: Literal["TookPlaceIn"] 68 | obj: Union[City, Country] = synalinks.Field( 69 | description="The city or country entity where the event occurred.", 70 | ) 71 | -------------------------------------------------------------------------------- /synalinks/src/knowledge_bases/knowledge_base_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from typing import Literal 4 | from unittest.mock import patch 5 | 6 | import numpy as np 7 | 8 | from synalinks.src import testing 9 | from synalinks.src.backend import Entity 10 | from synalinks.src.backend import Relation 11 | from synalinks.src.embedding_models import EmbeddingModel 12 | from synalinks.src.knowledge_bases import KnowledgeBase 13 | 14 | 15 | class Document(Entity): 16 | label: Literal["Document"] 17 | text: str 18 | 19 | 20 | class Chunk(Entity): 21 | label: Literal["Chunk"] 22 | text: str 23 | 24 | 25 | class IsPartOf(Relation): 26 | subj: Chunk 27 | label: Literal["IsPartOf"] 28 | obj: Document 29 | 30 | 31 | class KnowledgeBaseTest(testing.TestCase): 32 | @patch("litellm.aembedding") 33 | async def test_knowledge_base(self, mock_embedding): 34 | expected_value = np.random.rand(1024) 35 | mock_embedding.return_value = {"data": [{"embedding": expected_value}]} 36 | 37 | embedding_model = EmbeddingModel( 38 | model="ollama/mxbai-embed-large", 39 | ) 40 | 41 | knowledge_base = KnowledgeBase( 42 | uri="neo4j://localhost:7687", 43 | entity_models=[Document, Chunk], 44 | relation_models=[IsPartOf], 45 | embedding_model=embedding_model, 46 | metric="cosine", 47 | wipe_on_start=False, 48 | ) 49 | 50 | _ = await knowledge_base.query("RETURN 1") 51 | 52 | @patch("litellm.aembedding") 53 | def test_knowledge_base_serialization(self, mock_embedding): 54 | expected_value = np.random.rand(1024) 55 | mock_embedding.return_value = {"data": [{"embedding": expected_value}]} 56 | 57 | embedding_model = EmbeddingModel( 58 | model="ollama/mxbai-embed-large", 59 | ) 60 | 61 | knowledge_base = KnowledgeBase( 62 | uri="neo4j://localhost:7687", 63 | entity_models=[Document, Chunk], 64 | relation_models=[IsPartOf], 65 | embedding_model=embedding_model, 66 | metric="cosine", 67 | wipe_on_start=False, 68 | ) 69 | 70 | config = knowledge_base.get_config() 71 | cloned_knowledge_base = KnowledgeBase.from_config(config) 72 | self.assertEqual( 73 | cloned_knowledge_base.get_config(), 74 | knowledge_base.get_config(), 75 | ) 76 | -------------------------------------------------------------------------------- /examples/knowledge/extraction/relations_only_one_stage_kg_extraction.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | from typing import List, Union 4 | 5 | from knowledge_graph_schema import City, Country, Place, Event 6 | from knowledge_graph_schema import IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn 7 | from knowledge_dataset import Document, load_data 8 | 9 | 10 | class KnowledgeRelations(synalinks.Relations): 11 | relations: List[Union[IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn]] = ( 12 | synalinks.Field( 13 | description=( 14 | "A comprehensive list of relations including IsCapitalOf, IsLocatedIn," 15 | " IsCityOf, and TookPlaceIn, which describe interactions and associations" 16 | " between entities." 17 | ), 18 | ) 19 | ) 20 | 21 | 22 | async def main(): 23 | language_model = synalinks.LanguageModel( 24 | model="ollama/mistral", 25 | ) 26 | 27 | embedding_model = synalinks.EmbeddingModel( 28 | model="ollama/mxbai-embed-large", 29 | ) 30 | 31 | knowledge_base = synalinks.KnowledgeBase( 32 | uri="memgraph://localhost:7687", 33 | entity_models=[City, Country, Place, Event], 34 | relation_models=[IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn], 35 | embedding_model=embedding_model, 36 | metric="cosine", 37 | wipe_on_start=True, 38 | ) 39 | 40 | inputs = synalinks.Input(data_model=Document) 41 | knowledge_graph = await synalinks.Generator( 42 | data_model=KnowledgeRelations, 43 | language_model=language_model, 44 | )(inputs) 45 | 46 | embedded_knowledge_graph = await synalinks.Embedding( 47 | embedding_model=embedding_model, 48 | in_mask=["name"], 49 | )(knowledge_graph) 50 | 51 | outputs = await synalinks.UpdateKnowledge( 52 | knowledge_base=knowledge_base, 53 | )(embedded_knowledge_graph) 54 | 55 | program = synalinks.Program( 56 | inputs=inputs, 57 | outputs=outputs, 58 | name="relations_only_one_stage_extraction", 59 | description="A one stage KG extraction pipeline that extract only the relations", 60 | ) 61 | 62 | synalinks.utils.plot_program( 63 | program, 64 | to_folder="examples/knowledge/extraction", 65 | show_trainable=True, 66 | ) 67 | 68 | dataset = load_data() 69 | 70 | await program.predict(dataset, batch_size=1) 71 | 72 | 73 | if __name__ == "__main__": 74 | asyncio.run(main()) 75 | -------------------------------------------------------------------------------- /synalinks/src/modules/merging/logical_xor_test.py: -------------------------------------------------------------------------------- 1 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 2 | 3 | from synalinks.src import testing 4 | from synalinks.src.backend import DataModel 5 | from synalinks.src.modules.core.input_module import Input 6 | from synalinks.src.modules.merging.logical_xor import Xor 7 | from synalinks.src.programs.program import Program 8 | 9 | 10 | class LogicalXorTest(testing.TestCase): 11 | async def test_logical_xor_module_all_not_none(self): 12 | class Query(DataModel): 13 | query: str 14 | 15 | i0 = Input(data_model=Query) 16 | i1 = Input(data_model=Query) 17 | i2 = Input(data_model=Query) 18 | output = await Xor()([i0, i1, i2]) 19 | 20 | program = Program( 21 | inputs=[i0, i1, i2], 22 | outputs=output, 23 | ) 24 | 25 | result = await program( 26 | [ 27 | Query(query="a"), 28 | Query(query="b"), 29 | Query(query="c"), 30 | ] 31 | ) 32 | 33 | self.assertEqual(result, None) 34 | 35 | async def test_logical_xor_module_with_two_not_none(self): 36 | class Query(DataModel): 37 | query: str 38 | 39 | i0 = Input(data_model=Query) 40 | i1 = Input(data_model=Query) 41 | i2 = Input(data_model=Query) 42 | output = await Xor()([i0, i1, i2]) 43 | 44 | program = Program( 45 | inputs=[i0, i1, i2], 46 | outputs=output, 47 | ) 48 | 49 | result = await program( 50 | [ 51 | None, 52 | Query(query="b"), 53 | Query(query="c"), 54 | ] 55 | ) 56 | 57 | self.assertEqual(result, None) 58 | 59 | async def test_logical_xor_module_none(self): 60 | class Query(DataModel): 61 | query: str 62 | 63 | i0 = Input(data_model=Query) 64 | i1 = Input(data_model=Query) 65 | i2 = Input(data_model=Query) 66 | output = await Xor()([i0, i1, i2]) 67 | 68 | program = Program( 69 | inputs=[i0, i1, i2], 70 | outputs=output, 71 | ) 72 | 73 | result = await program( 74 | [ 75 | None, 76 | Query(query="b"), 77 | None, 78 | ] 79 | ) 80 | 81 | expected_json = { 82 | "query": "b", 83 | } 84 | 85 | self.assertEqual(result.get_json(), expected_json) 86 | -------------------------------------------------------------------------------- /examples/knowledge/retrieval/knowledge_augmented_generation.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | 4 | from knowledge_graph_schema import City, Country, Place, Event 5 | from knowledge_graph_schema import IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn 6 | 7 | 8 | class Query(synalinks.DataModel): 9 | query: str = synalinks.Field( 10 | description="The user query", 11 | ) 12 | 13 | 14 | class Answer(synalinks.DataModel): 15 | answer: str = synalinks.Field( 16 | description="The answer to the user query", 17 | ) 18 | 19 | 20 | async def main(): 21 | language_model = synalinks.LanguageModel( 22 | model="ollama/mistral", 23 | ) 24 | 25 | embedding_model = synalinks.EmbeddingModel( 26 | model="ollama/mxbai-embed-large", 27 | ) 28 | 29 | knowledge_base = synalinks.KnowledgeBase( 30 | uri="memgraph://localhost:7687", 31 | entity_models=[City, Country, Place, Event], 32 | relation_models=[IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn], 33 | embedding_model=embedding_model, 34 | metric="cosine", 35 | wipe_on_start=False, 36 | ) 37 | 38 | inputs = synalinks.Input(data_model=Query) 39 | query_result = await synalinks.TripletRetriever( 40 | entity_models=[City, Country, Place, Event], 41 | relation_models=[IsCapitalOf, IsLocatedIn, IsCityOf, TookPlaceIn], 42 | knowledge_base=knowledge_base, 43 | language_model=language_model, 44 | return_inputs=True, 45 | return_query=True, 46 | )(inputs) 47 | outputs = await synalinks.Generator( 48 | data_model=Answer, 49 | language_model=language_model, 50 | instructions=[ 51 | "Your task is to answer in natural language to the query based on the results of the search", 52 | "If the result of the search is not relevant, just say that you don't know", 53 | ], 54 | return_inputs=True, 55 | )(query_result) 56 | 57 | program = synalinks.Program( 58 | inputs=inputs, 59 | outputs=outputs, 60 | name="simple_kag", 61 | description="A simple KAG program", 62 | ) 63 | 64 | synalinks.utils.plot_program( 65 | program, 66 | to_folder="examples/knowledge/retrieval", 67 | show_trainable=True, 68 | show_schemas=True, 69 | ) 70 | 71 | result = await program(Query(query="What is the French capital?")) 72 | 73 | print(result.prettify_json()) 74 | 75 | 76 | if __name__ == "__main__": 77 | asyncio.run(main()) 78 | -------------------------------------------------------------------------------- /examples/agents/autonomous_function_calling_agent.py: -------------------------------------------------------------------------------- 1 | import synalinks 2 | import asyncio 3 | 4 | synalinks.enable_logging() 5 | 6 | 7 | class Query(synalinks.DataModel): 8 | query: str = synalinks.Field( 9 | description="The user query", 10 | ) 11 | 12 | 13 | class NumericalFinalAnswer(synalinks.DataModel): 14 | final_answer: float = synalinks.Field( 15 | description="The correct final numerical answer", 16 | ) 17 | 18 | 19 | @synalinks.utils.register_synalinks_serializable() 20 | async def calculate(expression: str): 21 | """Calculate the result of a mathematical expression. 22 | 23 | Args: 24 | expression (str): The mathematical expression to calculate, such as 25 | '2 + 2'. The expression can contain numbers, operators (+, -, *, /), 26 | parentheses, and spaces. 27 | """ 28 | if not all(char in "0123456789+-*/(). " for char in expression): 29 | return { 30 | "result": None, 31 | "log": ( 32 | "Error: invalid characters in expression. " 33 | "The expression can only contain numbers, operators (+, -, *, /)," 34 | " parentheses, and spaces NOT letters." 35 | ), 36 | } 37 | try: 38 | # Evaluate the mathematical expression safely 39 | result = round(float(eval(expression, {"__builtins__": None}, {})), 2) 40 | return { 41 | "result": result, 42 | "log": "Successfully executed", 43 | } 44 | except Exception as e: 45 | return { 46 | "result": None, 47 | "log": f"Error: {e}", 48 | } 49 | 50 | 51 | async def main(): 52 | language_model = synalinks.LanguageModel( 53 | model="ollama/mistral", 54 | ) 55 | 56 | tools = [ 57 | synalinks.Tool(calculate), 58 | ] 59 | 60 | inputs = synalinks.Input(data_model=Query) 61 | outputs = await synalinks.FunctionCallingAgent( 62 | data_model=NumericalFinalAnswer, 63 | tools=tools, 64 | language_model=language_model, 65 | max_iterations=5, 66 | return_inputs_with_trajectory=True, 67 | autonomous=True, 68 | )(inputs) 69 | agent = synalinks.Program( 70 | inputs=inputs, 71 | outputs=outputs, 72 | name="math_agent", 73 | description="A math agent", 74 | ) 75 | 76 | input_query = Query(query="How much is 152648 + 485?") 77 | response = await agent(input_query) 78 | 79 | print(response.prettify_json()) 80 | 81 | 82 | if __name__ == "__main__": 83 | asyncio.run(main()) 84 | -------------------------------------------------------------------------------- /synalinks/src/backend/common/symbolic_data_model_test.py: -------------------------------------------------------------------------------- 1 | # Modified from: keras/src/backend/common/keras_tensor_test.py 2 | # Original authors: François Chollet et al. (Keras Team) 3 | # License Apache 2.0: (c) 2025 Yoan Sallami (Synalinks Team) 4 | 5 | from synalinks.src import testing 6 | from synalinks.src.backend import DataModel 7 | from synalinks.src.backend import standardize_schema 8 | from synalinks.src.backend.common import SymbolicDataModel 9 | 10 | 11 | class SymbolicDataModelTest(testing.TestCase): 12 | def test_constructor_with_schema(self): 13 | class Query(DataModel): 14 | query: str 15 | 16 | x = SymbolicDataModel(schema=Query.get_schema()) 17 | self.assertEqual( 18 | x.get_schema(), 19 | standardize_schema(Query.get_schema()), 20 | ) 21 | 22 | def test_constructor_with_datatype(self): 23 | class Query(DataModel): 24 | query: str 25 | 26 | x = SymbolicDataModel(data_model=Query) 27 | self.assertEqual( 28 | x.get_schema(), 29 | standardize_schema(Query.get_schema()), 30 | ) 31 | 32 | def test_constructor_without_args(self): 33 | with self.assertRaisesRegex( 34 | ValueError, 35 | "You should specify at least one argument between `data_model` or `schema`", 36 | ): 37 | _ = SymbolicDataModel() 38 | 39 | def test_representation(self): 40 | class Query(DataModel): 41 | query: str 42 | 43 | x = SymbolicDataModel(schema=Query.get_schema()) 44 | self.assertIn( 45 | f"