Tutorial 2: Building STL Programmatically
Learn how to construct STL statements in code using the builder API, without writing raw text.
What you’ll learn:
- Build single statements with
stl() - Set modifiers with
.mod() - Build multi-statement documents with
stl_doc() - Use namespaces and custom modifiers
- Convert built statements to text and JSON
Prerequisites: Tutorial 1: Parsing
Step 1: Build a Simple Statement
from stl_parser import stl
stmt = stl("[Rain]", "[Flooding]").build()
print(str(stmt))
# [Rain] -> [Flooding]
The stl() function takes source and target anchor strings, returning a StatementBuilder. Call .build() to produce the final Statement object.
Step 2: Add Modifiers
Chain .mod() to add modifier key-value pairs:
stmt = (
stl("[Rain]", "[Flooding]")
.mod(rule="causal", confidence=0.85, strength=0.8)
.build()
)
print(str(stmt))
# [Rain] -> [Flooding] ::mod(confidence=0.85, rule="causal", strength=0.8)
You can call .mod() multiple times — later calls override earlier values for the same key:
stmt = (
stl("[A]", "[B]")
.mod(confidence=0.5)
.mod(confidence=0.9, rule="causal") # overrides confidence
.build()
)
print(stmt.modifiers.confidence) # 0.9
Step 3: Anchor Formats
The builder accepts several anchor formats:
# With brackets
stl("[Rain]", "[Flooding]")
# Without brackets (added automatically)
stl("Rain", "Flooding")
# With namespace
stl("Physics:Energy", "Physics:Mass")
stl("[Physics:Energy]", "[Physics:Mass]")
All produce valid statements with properly constructed Anchor objects.
Step 4: Build Multi-Statement Documents
Use stl_doc() to combine multiple builders into a ParseResult:
from stl_parser import stl, stl_doc
doc = stl_doc(
stl("[Rain]", "[Flooding]").mod(rule="causal", confidence=0.85),
stl("[Flooding]", "[Evacuation]").mod(rule="causal", confidence=0.90),
stl("[Evacuation]", "[Safety]").mod(rule="causal", confidence=0.95),
)
print(f"Statements: {len(doc.statements)}") # 3
print(doc.is_valid) # True
for s in doc.statements:
print(f" {s}")
You can mix StatementBuilder and pre-built Statement objects:
pre_built = stl("[A]", "[B]").mod(confidence=0.9).build()
doc = stl_doc(
pre_built, # Statement object
stl("[C]", "[D]").mod(rule="logical"), # StatementBuilder
)
Step 5: Custom Modifiers
Any key not in the standard modifier set is stored as a custom modifier:
stmt = (
stl("[Drug_Aspirin]", "[Effect_PainRelief]")
.mod(confidence=0.92, rule="causal", dosage="500mg", trial_id="NCT123")
.build()
)
print(stmt.modifiers.confidence) # 0.92 (standard)
print(stmt.modifiers.custom) # {"dosage": "500mg", "trial_id": "NCT123"}
Step 6: Validation
The builder validates modifier values at two levels:
- Pydantic field validation — enforces type and range constraints (e.g., confidence 0.0-1.0)
- Semantic validation — checks for contradictions and completeness (via
.build())
from stl_parser import stl
# This raises STLBuilderError — confidence must be 0.0-1.0
try:
stl("[A]", "[B]").mod(confidence=1.5).build()
except Exception as e:
print(f"Error: {e}")
Use .no_validate() to skip semantic validation (Step 2), while Pydantic field constraints still apply:
# Skips semantic validation, but Pydantic still enforces field ranges
stmt = stl("[A]", "[B]").mod(confidence=0.95).no_validate().build()
Step 7: Serialize Built Statements
Built statements work with all serialization functions:
from stl_parser import stl, stl_doc, to_json, to_stl
from stl_parser.serializer import to_rdf
doc = stl_doc(
stl("[Einstein]", "[Relativity]").mod(rule="empirical", confidence=0.98),
stl("[Relativity]", "[TimeDilation]").mod(rule="logical", confidence=0.99),
)
# To STL text
print(to_stl(doc))
# To JSON
print(to_json(doc, indent=2))
# To RDF/Turtle
print(to_rdf(doc, format="turtle"))
Step 8: Build with Namespaces
doc = stl_doc(
stl("[Physics:Energy]", "[Physics:Mass]").mod(rule="logical", confidence=0.99),
stl("[Psychology:Energy]", "[Psychology:Motivation]").mod(rule="causal", confidence=0.80),
)
for s in doc.statements:
print(f" {s.source.namespace}:{s.source.name} -> {s.target.name}")
Complete Example
from stl_parser import stl, stl_doc, to_json
# Build a causal chain about climate
doc = stl_doc(
stl("[CO2_Emissions]", "[Greenhouse_Effect]")
.mod(rule="causal", confidence=0.95, strength=0.90,
source="IPCC_AR6_2021"),
stl("[Greenhouse_Effect]", "[Global_Warming]")
.mod(rule="causal", confidence=0.93, strength=0.85),
stl("[Global_Warming]", "[Sea_Level_Rise]")
.mod(rule="causal", confidence=0.88, strength=0.75,
time="2100", domain="climate_science"),
stl("[Global_Warming]", "[Extreme_Weather]")
.mod(rule="causal", confidence=0.85, strength=0.70),
)
print(f"Built {len(doc.statements)} statements\n")
# Print as STL
for s in doc.statements:
print(s)
# Export as JSON
print("\n" + to_json(doc, indent=2))