Install
$ agentstack add skill-relationalai-rai-agent-skills-rai-pyrel-coding ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
PyRel Coding
Summary
What: PyRel v1 language syntax — imports, types, concepts, properties, expressions, data loading, and standard library.
When to use:
- Writing or reviewing PyRel model code
- Need the correct import path, type syntax, or property declaration pattern
- Debugging a syntax error,
UninitializedPropertyException, or empty/unexpected query results - Checking what standard library functions are available (math, strings, dates)
- Looking up data loading patterns (CSV, Snowflake, FK resolution)
- Understanding expression rules (
.where()targets,.per()grouping, operators)
When NOT to use: This is general-syntax support. Any reasoner-specific task routes to its reasoner skill — when the task is to query, classify, optimize, analyze a graph, or train a model, load that skill (it owns the patterns and pitfalls), not this one.
- Query construction (select, aggregation, filtering, joins) — see
rai-querying - Business-rule authoring via derived properties (validation, classification, segmentation, alerting) — see
rai-rules-authoring - Optimization formulation (decision variables, constraints, objectives) — see
rai-prescriptive-problem-formulation - Graph analysis (centrality, community, reachability, paths, impact/"what's affected if X fails") — see
rai-graph-analysis - GNN / predictive modeling and training (features, edges, training, evaluation) — see
rai-predictive-modeling,rai-predictive-training - RAI domain modeling decisions (when to create a concept vs property, gap classification) — see
rai-ontology-design - Connection and config setup — see
rai-setup
Overview: Reference skill. Key lookup areas: Imports, Model Patterns, Type System, Concepts/Properties/Relationships, Data Loading, References and Aliasing, Standard Library (math/strings/dates), Expression Rules.
Quick Reference
# Imports
from relationalai.semantics import (
Model, Float, Integer, String, Date, DateTime,
count, sum, min, max, avg, product, stddev_samp, per, distinct,
)
from relationalai.semantics import Number # Always use Number.size(p,s) — bare Number causes inference issues
# Model + concepts
model = Model("my_model")
Product = model.Concept("Product", identify_by={"id": Integer})
# Properties (concept → value) and Relationships (concept → concept)
Product.cost = model.Property(f"{Product} has {Float:cost}")
Product.supplier = model.Property(f"{Product} supplied by {Supplier:supplier}")
# Data loading
model.define(Product.new(model.data(df).to_schema()))
# Query — prefer model.where/model.select for multi-model safety
result = model.where(Product.cost > 10).select(Product.id, Product.cost).to_df()
# Debugging — print() shows readable PyRel structure, .inspect() materializes data
print(Product.cost) # → Product.cost
print(Product.cost > 10) # → Product.cost > 10
Product.cost.inspect() # → executes query, prints DataFrame to stdout
For solver formulation (Problem, solve_for, satisfy, minimize/maximize), see rai-prescriptive-problem-formulation. For CSP-style formulation specifically (Problem(model, Integer) + solver="minizinc", all-discrete decisions and data, globals, multi-solution enumeration), see rai-prescriptive-problem-formulation/references/csp-formulation.md.
Imports
When you need imports beyond the common ones shown in Quick Reference (reasoners, std library, non-obvious aliases, or avoiding builtin shadowing), see [imports.md](references/imports.md).
Model Patterns
model = Model("my_model")
Accepted parameters: name (str), config (Config), exclude_core (bool), is_library (bool). Without config, auto-discovers raiconfig.yaml. Use time_ns() suffix only for throwaway models in automated pipelines. Shorthand: Concept, Property = model.Concept, model.Property.
Always use model.define()/model.where()/model.select() — standalone define(), where(), select() fail when multiple Models exist ("Multiple Models have been defined."). Generated code should always use the model method form.
Type System
Types are imported objects, not strings. Used in Property f-strings and as type arguments to reasoner APIs.
| Type | Usage in Property | Python equivalent | |------|-------------------|-------------------| | Float | {Float:cost} | float | | Integer | {Integer:count} | int | | String | {String:name} | str | | Number.size(p,s) | {Number.size(38,4):price} | decimal(p,s) | | Date | {Date:due_date} | date | | DateTime | {DateTime:timestamp} | datetime | | Boolean | {Boolean:active} | bool |
Number.size(precision, scale) — always use with explicit parameters. Number alone (unparameterized) causes type inference issues and should be avoided. Always specify Number.size(p, s) (e.g., Number.size(38, 4) for Snowflake NUMBER columns). Do NOT call Number(38, 4) directly (that's concept invocation, not type construction).
Deprecated alias: decimal is a deprecated alias for Number — do not use it. Use Number.size(p, s) instead.
Snowflake type alignment: When loading from Snowflake tables, the property type must match the column's actual data type. Verify with DESCRIBE TABLE or INFORMATION_SCHEMA.COLUMNS. See [rai-ontology-design](../rai-ontology-design/SKILL.md) for the full mapping table and [rai-build-starter-ontology](../rai-build-starter-ontology/SKILL.md) for the validation workflow.
Properties and Relationships — When to Use Which
The choice is about multiplicity — whether the association is many-to-one or many-to-many. Both support any arity (unary, binary, ternary+).
Property — functional dependency (uniqueness constraint). Inputs uniquely determine the output (at most one value). Use for many-to-one. Provides performance benefits and enforces the constraint.
Relationship — no uniqueness constraint (zero, one, or many outputs per input). Use for many-to-many. More flexible when cardinality is uncertain, but prefer establishing multiplicity.
| Pattern | Use Property | Use Relationship | |---------|-------------|-----------------| | Single-valued attribute | Food has one Float:cost | — | | Concept → Concept (functional, e.g. FK) | Order placed by exactly one Customer | — | | Concept → Concept (multi-valued) | — | Parent has many Child | | Availability/membership (many-to-many) | — | Worker available for Shift | | Unary flag (functional) | Order is rush order | — | | Ternary with FD | Food contains Nutrient in Float:qty | — | | Ternary without FD | — | Food contains Nutrient (many-to-many) | | Association where cardinality is uncertain | — | Use Relationship — but prefer establishing multiplicity |
Concepts
# Identified concept (typed key — PREFERRED, use whenever possible)
Food = model.Concept("Food", identify_by={"name": String})
Stock = model.Concept("Stock", identify_by={"index": Integer})
Edge = model.Concept("Edge", identify_by={"i": Integer, "j": Integer})
# Basic concept (no identify_by — avoid unless extending primitives)
Product = model.Concept("Product")
# Subtype (inherits parent properties)
ActiveOrder = model.Concept("ActiveOrder", extends=[Order])
# Populate subtype membership with model.define():
model.define(ActiveOrder(Order)).where(Order.total > 75)
# Query through parent properties:
model.select(ActiveOrder.id, ActiveOrder.total)
# Extending a primitive type (no identify_by needed — identity comes from the primitive)
PositiveInt = model.Concept("PositiveInt", extends=[Integer])
- Always include
identify_bywhenever possible. It defines the natural key — entities with the same key values are the same instance. This prevents duplicate entities and makes identity explicit. - Without
identify_by, ALL parameters passed to.new()are used for the identity hash. This is fragile — adding or removing a.new()parameter changes entity identity. - Composite keys use multiple entries:
identify_by={"i": Integer, "j": Integer}. identify_byauto-creates properties.Concept("Customer", identify_by={"customer_id": Integer})automatically createsCustomer.customer_idas aProperty(Integer). Do not declare a separatemodel.Property()for identity fields — it will create a duplicate.identify_bysupports concept types — use for composite keys involving other concepts:OrderItem = model.Concept("OrderItem", identify_by={"order": Order, "item": Item}). This is standard for association/junction concepts.- Exception where
identify_byis not used: Extending primitive types (extends=[Integer]) — identity comes from the primitive value. - Introspection: Prefer
inspect.schema(model)fromrelationalai.semantics.inspect(v1.0.14+) — returns a frozenModelSchemawith concepts, inherited properties, types, and data sources. Useinspect.to_concept(obj)for reusable helpers that must accept any DSL handle (Chain, Ref, FieldRef, Expression). Lower-levelmodel.concepts/model.concept_index["Name"]remain available as a fallback. Seerai-querying/references/inspect-module.md.
Properties
Properties use f-strings with type references. The type comes BEFORE the field name: {Type:field}. Use Property for many-to-one (functional) associations — scalar attributes, functional concept-to-concept FKs, and N-ary associations with a functional dependency.
# Scalar value properties (concept → primitive)
Food.cost = model.Property(f"{Food} has {Float:cost}")
Worker.name = model.Property(f"{Worker} has {String:name}")
# Functional FK property (concept → concept, many-to-one)
Order.customer = model.Property(f"{Order} placed by {Customer:customer}")
# Multiarity property with value output (inputs uniquely determine the Float output)
Food.contains = model.Property(f"{Food} contains {Nutrient} in {Float:qty}")
Canonical syntax (v1): model.Property(f"{Food} has {Float:cost}") — f-string with type objects interpolated. Always use this form.
Property name vs f-string verb: The property name is the f-string field name (e.g., qty in {Float:qty}), not the verb (contains, in).
Multi-argument (multiarity) properties: Field names required when the same type appears multiple times to disambiguate inputs.
Stock.covar = model.Property(f"{Stock:stock1} and {Stock:stock2} have {Float:covar}") # Binary (same-type disambiguation)
ResourceGroup.inv = model.Property(f"{ResourceGroup} on day {Integer:t} has {Float:inv}") # Time-indexed
Worker.assignment = model.Property(f"{Worker} has {Shift} if {Integer:assigned}") # Multi-concept
Scalar / standalone properties (primitives only, no user-defined concepts): bin_fast = model.Property(f"departure day {Integer:t} has {Float:bin_fast}")
Don't pass short_name= at all — PyRel derives it from the LHS attribute name. If you do pass one that doesn't match (e.g. legacy prefixed names copied from older example files), declaration fails immediately with RAIException: [Invalid short name] ... has a mismatching short_name "x". Either omit short_name or set it to "". The silent-call hazard remains too: invoking via an alternate name (Concept.cf_foo(...) instead of Concept.foo(...)) creates a parallel implicit Property, producing NaN-everywhere reads on queries and UnresolvedType / [TyperError] on joins. Default to never passing short_name=; it's needed only for model.relationship_index[...] lookup and same-type-slot disambiguation. See [common-pitfalls.md](references/common-pitfalls.md).
Avoid reserved attribute names — Property/Relationship names that collide with reserved attributes on Concept (ref, new, select, where, define, filter_by, identify_by, …) raise [Reserved relationship name] at declaration. Common bites: ref (git refs, FK ref columns), new (versioning flag). Rename the Concept-side attribute (deploy_ref instead of ref); the source column doesn't have to change. Full reserved list in [common-pitfalls.md](references/common-pitfalls.md) — defer to the actual PyRel error message for the authoritative set, which may shift across releases.
Dynamic Property names need setattr. Declaring via model.Property(f"{Concept} has {Float:%s}" % attr) registers the Property with that field name, but getattr(Concept, attr) may surface as value type Any in solve_for(...). Bind the typed attribute explicitly: setattr(Concept, attr, prop). Required for scenario sweeps and any pattern that programmatically generates Property names.
Relationships
Relationships are multi-valued associations — zero, one, or many outputs per input. No uniqueness constraint. Use when an input can have many outputs, or the association has multiple fields.
# Multi-valued concept-to-concept (one parent → many children)
Parent.has_child = model.Relationship(f"{Parent} has {Child}")
# Availability / membership (many-to-many)
Worker.available_for = model.Relationship(f"{Worker} is available for {Shift}")
# Functional concept-to-concept FKs use Property, NOT Relationship —
# see Properties section above. Example: Order.customer = model.Property(
# f"{Order} placed by {Customer:customer}") because each Order has exactly one Customer.
Scalar variables (standalone floats/ints without a parent concept). Use for optimization variables not attached to any concept (e.g., NLP problems with just a few free variables):
x = model.Relationship(f"{Float:x}")
y = model.Relationship(f"{Float:y}")
For how to bind these to a solver, see rai-prescriptive-problem-formulation.
Global counts/values: Assign to a Python variable (NODE_COUNT = count(Node)). Use a model Relationship only when the value must be available in downstream define() rules or solver expressions: model.define(node_count(count(Node))).
Bracket access: Use rel["field"] to access named roles. See References and Aliasing section below.
Chain.ref() and .alt(): For independent chain traversals and inverse relationships, see [expression-rules.md](references/expression-rules.md#chainref-and-alt).
Definitions
Definitions are the core of PyRel coding. They let you bake business logic and domain knowledge into the model so that every query and solver formulation can leverage it. Prefer putting logic in definitions over writing complex queries.
model.define() — declare facts, computed properties, derived relationships, and entity creation rules:
# Computed property — derived from existing data
model.define(Order.total(Order.quantity * Order.unit_price))
# Boolean flag as unary relationship
model.define(Shipment.is_delayed()).where(Shipment.delay_days > 0)
# Derived relationship — named subset
# NOTE: aggregation needs an explicit join through the relationship
high_value = model.Relationship(f"High Value: {Customer}")
model.define(high_value(Customer)).where(
aggs.sum(Order.total).where(Order.customer(Customer)).per(Customer) > 10000
)
# Entity creation from relationships
model.define(
OrderItem.new(order=Order, item=Item)
).where(Order.contains(Item))
# Conditional / multi-branch definitions
model.define(Order.priority_label("high")).where(Order.total > 1000)
model.define(Order.priority_label("low")).where(Order.total =1.12`, as constants in every value position: kwargs (`Account.new(status=Status.ACTIVE)`, `Account.lookup(status=...)`), enum-typed property values, and prescriptive `solve_for()`/`satisfy()` expressions. Use instead of bare string comparisons when values are a fixed, closed vocabulary — and only where members are a clear readability or usability win; leave open-ended, data-driven string fields (entity names, free-form codes) as `String`.
```python
# Class-style:
class Priority(model.Enum):
LOW = "low"
MEDIUM
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [RelationalAI](https://github.com/RelationalAI)
- **Source:** [RelationalAI/rai-agent-skills](https://github.com/RelationalAI/rai-agent-skills)
- **License:** Apache-2.0
- **Homepage:** https://relational.ai
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.