├── 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 |
--------------------------------------------------------------------------------
/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"