Skip to content

What's New in Version 1.18.0

June 30, 2026 9:12 PM UTC

Version 1.18.0 of the relationalai Python package is now available!

To upgrade, activate your virtual environment and run the following command:

Terminal window
pip install --upgrade relationalai
  • rai doctor report now generates a deterministic diagnosis.md summary and shows the same diagnosis in the debugger report browser. Before 1.18.0, you had to inspect the collected artifacts yourself to identify likely configuration, Snowflake, deploy, schema-cache, trace, or collection problems.

  • PyRel now fails fast on undefined config references instead of ignoring them or failing later at runtime. For example, a typo in active_profile such as active_profile: localm now raises a clear validation error during config loading.

  • Fixed math.clip() so it returns the clipped value instead of NULL when the input falls below, within, or above the requested bounds.

  • Fixed how reasoner names are generated. If you don’t set custom reasoner names in your configuration, provisioning could previously fail when your username starts with a digit because the generated reasoner name was invalid. PyRel now sanitizes the generated name automatically, for example by turning 12345 into user_12345.

  • Fixed nested select() values inside union and match branches, and fixed aggregates over scalar subquery values, so those query shapes return the expected rows instead of raising internal compiler or validation errors.

  • Fixed rank() queries that also return a related value in the same result. Those queries now return the expected table and use NULL when the related value is missing instead of failing validation.

  • PyRel now raises an Ambiguous concept usage error when one rule reuses the same concept name as both a variable and a concept type in the same line. For example, the following code now raises an error instead of silently misinterpreting the second line:

    from relationalai.semantics import Model, String
    m = Model("ambiguous")
    A = m.Concept("A")
    B = m.Concept("B", extends=[A])
    C = m.Concept("C", extends=[A])
    D = m.Concept("D", extends=[B, C])
    A.id = m.Property(f"{A} is identified by {String}")
    A.identify_by(A.id)
    m.define(A.new(m.data([
    {"id": "w"},
    {"id": "x"},
    {"id": "y"},
    {"id": "z"},
    ]).to_schema()))
    m.define(B(A)).where(A.id.in_(["w", "x", "z"]))
    m.define(C(A)).where(A.id.in_(["w", "y", "z"]))
    # Unambiguous: B is a variable in both places.
    m.define(D(B)).where(C(B))
    # Ambiguous: B is variable in D(B) but a concept type in B(C).
    m.define(D(B)).where(B(C)) # now raises "Ambiguous concept usage"

    Previously, PyRel would silently define every entity of type B as a D in this case because the type expression B(C) doesn’t constrain the variable B in .define() in any way.