Skip to content

What's New in Version 1.27.0

August 18, 20266:31 PM UTC

Version 1.27.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
  • When a concept or relationship is built directly from your source Snowflake tables — without any additional computation in PyRel — model deployment now creates it as a SQL view instead of a table, which avoids unnecessary storage and refresh work. If you need it stored as a table, set type: table for that output under deployment.outputs.overrides. See Choose how outputs are materialized for details.

  • Redeploying a model now keeps existing deployed tables’ data and Snowflake grants where possible, instead of dropping and recreating the tables. Views and dynamic tables are still recreated, but their grants are now preserved too. Some changes, such as changing an output column’s type, can still require recreating a table.

  • Model deployment now tags each schema it creates as RAI-managed, which supports safer cleanup later. If your deployment role can’t apply that tag, deployment now stops with a clear error telling you to grant APPLY on the tag (or APPLY TAG on the account) instead of leaving an untagged schema behind.

  • Temporary tables created by PyRel when deploying a model now live in the deployment’s _META schema instead of appearing next to your model outputs, keeping the target schema limited to your model’s objects.

  • common.hash() now produces different values than in earlier releases. This matters mainly when a hash is saved rather than computed on the fly — for example, when a deployed model materializes a property or relationship whose value comes from a hash, or when you export query results that include a hash to a table. Before upgrading, search your model code for common.hash( and plan to recompute any stored hashes that must stay stable.

  • In a deployed model, common.parse_uuid() now works consistently. A UUID that you parse and then compare against an entity in your model now matches instead of silently returning no rows, and common.uuid_to_string() applied to a parsed UUID now returns a plain 32-character value instead of dashed UUID text. Pass UUIDs in the standard dashed form, and update anything that expected the dashed output.

  • A query that uses one select() inside another expression now returns the correct rows. Previously, a nested selection could drop rows or ignore its own filter.

  • Selecting a constant value in a query no longer fails with a GROUP BY term out of range error when combined with other properties and relationships, and now returns the expected rows.

  • Definitions that negate a union() with not_() or supply fallback values with | no longer fail to compile when deploying a model.

  • If you force two deployment outputs that depend on each other onto different reasoners, deployment now fails with a clear ConflictingForcedReasoners error that names the outputs and how to fix them, instead of a raw internal error. You set an output’s reasoner with deployment.outputs.overrides.

  • When rai models list can’t read the model-management schema because of your role’s privileges, the error now recommends the correct GRANT USAGE statements instead of an APPLY ON TAG grant that didn’t fix the problem.

  • After running the rai models teardown CLI command with the --force option, the follow-up message no longer points you to unnecessary or unavailable commands.

  • Calling a concept with keyword arguments, such as Movie(title="The Matrix") instead of Movie.lookup(title="The Matrix"), now raises a clear Invalid usage error right away instead of failing at query time with a confusing AssertionError.

  • Deploying a concept whose identity includes another concept that has a composite (multi-field) identity key no longer drops part of that key. Previously the deploy could fail with a NULL result in a non-nullable column error, or store incomplete identities.

  • When you create a decision variable with Problem.solve_for() for a concept that has no identify_by key, querying that variable’s solved values now returns the matching rows instead of nothing.