Data Model Operators
Data Model Operators
In previous lessons, you learned to create branches where some outputs can be
None. How do we combine these outputs? This lesson introduces operators
for merging and manipulating data models.
The Five Operators
graph LR
subgraph Operators
CONCAT[A + B: Concatenate]
AND[A & B: Safe Merge]
OR[A | B: First Non-None]
XOR[A ^ B: Exclusive]
NOT[~A: Cancel]
end
Synalinks provides five Python operators for data models:
| Operator | Name | Behavior |
|---|---|---|
+ |
Concatenation | Merge fields (fails if either is None) |
& |
Logical AND | Safe merge (returns None if either is None) |
| |
Logical OR | Returns first non-None, or merges if both exist |
^ |
Logical XOR | Returns data only if exactly ONE is non-None |
~ |
NOT (Invert) | Converts data to None |
1. Concatenation (+)
Combines fields from two data models into one:
answer = Answer(answer="42") # {"answer": "42"}
thinking = Thinking(thinking="...") # {"thinking": "..."}
combined = answer + thinking # {"answer": "42", "thinking": "..."}
Warning: Raises an exception if either operand is None!
2. Logical AND (&)
Safe concatenation - returns None if either input is None:
3. Logical OR (|)
Returns non-None value, or merges if both have data:
(easy_result, hard_result) = await Branch(...)(inputs)
final = easy_result | hard_result # Gets whichever has data
4. Logical XOR (^)
Returns data only if exactly one operand is non-None:
result = a ^ b
# If a has data and b is None: returns a
# If a is None and b has data: returns b
# If both have data: returns None
# If both are None: returns None
5. NOT / Invert (~)
Converts any data to None:
Useful for conditional flows where you want to "cancel" a path.
Truth Table
| A | B | A + B |
A & B |
A | B |
A ^ B |
|---|---|---|---|---|---|
| Data | Data | Merged | Merged | Merged | None |
| Data | None |
ERROR |
None |
A | A |
None |
Data | ERROR |
None |
B | B |
None |
None |
ERROR |
None |
None |
None |
| A | ~A |
|---|---|
| Data | None |
| None | None* |
*Note: ~None behavior depends on context (symbolic vs json)
Complete Example: Merging Branch Outputs
import asyncio
from dotenv import load_dotenv
import synalinks
class Query(synalinks.DataModel):
query: str = synalinks.Field(description="The user query")
class Answer(synalinks.DataModel):
answer: str = synalinks.Field(description="The correct answer")
async def main():
load_dotenv()
language_model = synalinks.LanguageModel(model="openai/gpt-4.1")
inputs = synalinks.Input(data_model=Query)
# Branch returns (easy_result, hard_result) - only ONE is non-None
(easy, hard) = await synalinks.Branch(
question="Evaluate difficulty",
labels=["easy", "difficult"],
language_model=language_model,
branches=[
synalinks.Generator(data_model=Answer, language_model=language_model),
synalinks.Generator(data_model=Answer, language_model=language_model),
],
)(inputs)
# Use | to get whichever branch was active
outputs = easy | hard
program = synalinks.Program(inputs=inputs, outputs=outputs)
result = await program(Query(query="What is 2 + 2?"))
print(f"Answer: {result['answer']}") # Gets the non-None result
asyncio.run(main())
Key Takeaways
- Concat (
+): Merge all fields from two data models into one. Both inputs must have data (noNone). - OR (
|): Returns the first non-None input. Perfect for merging conditional branch outputs. - AND (
&): Returns data only if both inputs have data; otherwise returnsNone. - XOR (
^): Returns data only if exactly one input has data; returnsNoneif both or neither have data. - NOT (
~): Always returnsNone. Useful for canceling paths in conditional flows.
Program Visualizations
API References
Answer
AnswerWithThinking
Bases: DataModel
An answer with reasoning.


