Skip to content

Configure deployment

Deploy the same model to different Snowflake schemas to keep its development, staging, and production outputs separate. You can also deploy one model to multiple target schemas simultaneously, for example to support tenants with isolated data sources. Each public schema holds that environment’s outputs, and its internal schema holds the deployment’s own objects, state, and refresh metadata. Name both: a deployment must say where each goes. Deploying to another schema does not move or copy the model’s source tables.

Configuration profiles are named overrides on your base configuration. Give each environment a profile that sets its deployment target. Add these settings to your existing raiconfig.yaml, keeping your connection configuration:

profile:
dev:
deployment:
public_schema: MY_DATABASE.MY_MODEL_DEV
identity:
internal_schema: MY_DATABASE.MY_MODEL_DEV_META
staging:
deployment:
public_schema: MY_DATABASE.MY_MODEL_STAGING
identity:
internal_schema: MY_DATABASE.MY_MODEL_STAGING_META
prod:
deployment:
public_schema: MY_DATABASE.MY_MODEL_PROD
identity:
internal_schema: MY_DATABASE.MY_MODEL_PROD_META
active_profile: dev
  • The dev, staging, and prod profiles select different output schemas for the same model.
  • Each one also names its own internal schema, by convention the public schema with a _META suffix.
  • active_profile selects the profile to load. Change it to staging or prod when you want that environment’s settings.
  • These profile names are conventions. Selecting a profile does not promote a model or copy data between environments.

Inspect the loaded configuration before deploying:

from relationalai.config import create_config
cfg = create_config()
print(cfg.active_profile)
print(cfg.deployment.public_schema)
Output
dev
MY_DATABASE.MY_MODEL_DEV

These values confirm that the dev profile selects the development target, not that the model has been deployed. If they differ, check the active profile and any overrides using Use profiles to manage multiple configurations. Then follow Deploy a model to deploy and verify the outputs.

The deployment section controls where and how a model deploys its outputs to Snowflake. For most models, you only need to set deployment.public_schema, deployment.identity.internal_schema, and deployment.role:

deployment:
public_schema: MY_DATABASE.MY_MODEL # Where the model's output views are created
identity:
internal_schema: MY_DATABASE.MY_MODEL_META # Where PyRel stores deployment metadata
role: MY_DEPLOYMENT_ROLE # Snowflake role used to deploy and refresh the model
# auto_deploy: true # Deploy automatically before running queries (defaults to false)

The auto_deploy setting is optional.

Here is what each setting does:

  • public_schema: The fully-qualified Snowflake schema (<DATABASE>.<SCHEMA>) that holds the model’s outputs. Each output is a view here, and these views are the model’s SQL interface: query them, grant on them, and attach policies to them. The objects that hold the data behind them live in the internal schema. Models can use dedicated schemas or share a schema with object name prefixes or suffixes. PyRel will not overwrite objects that do not belong to this model. So if an object belonging to you or another model has the same name as a model output, the deploy fails until you resolve the name conflict.
  • role: The Snowflake role PyRel uses to deploy and refresh the model. If you omit it, PyRel uses the connection or current session role. Refresh tasks run with this role alone, excluding Snowflake secondary roles. This role must have every privilege that deployment and refresh need, for example USAGE on the warehouse. Privileges it inherits through Snowflake’s role hierarchy count, but privileges gained via secondary roles do not.
  • identity.internal_schema: The schema where PyRel keeps the deployment’s own internal objects and the metadata that manages them, such as stored procedures, the refresh plan table, and the refresh log. It also holds the objects that store the data behind the public views, named with an _INTERNAL_STORE_ prefix. Keeping it separate from public_schema prevents PyRel’s bookkeeping from mixing with your model’s outputs. Treat everything here as PyRel’s own: it can change between releases, so do not query it or depend on its shape. It is required, so the schema PyRel creates these objects in is always one you chose. By convention it is the public schema with a _META suffix, for example MY_DATABASE.MY_MODEL_META.
  • auto_deploy: When true, PyRel deploys the model automatically before it runs queries against it, so you do not have to run rai models deploy yourself. It defaults to false, which means you must deploy explicitly.

Configure object name prefixes and suffixes

Section titled “Configure object name prefixes and suffixes”

Set deployment.identity.object_name_prefix or deployment.identity.object_name_suffix to customize the names of objects that PyRel creates for your model. Both settings default to an empty string. You can set either value or both. For example, add these fields to your existing deployment configuration:

deployment:
public_schema: MY_DATABASE.SHARED_MODELS
identity:
internal_schema: MY_DATABASE.SHARED_MODELS_META
object_name_prefix: PRJ123_
object_name_suffix: _DEV

An output named TRANSACTION becomes PRJ123_TRANSACTION_DEV. PyRel also applies prefixes and suffixes to internal objects. For example, the refresh status procedure REFRESH_STATUS() becomes PRJ123_REFRESH_STATUS_DEV(). Source tables and the ownership tag’s name do not change.

The naming rules are:

  • Values are converted to uppercase. PyRel adds no separator, so include underscores where you want them.
  • Values can contain ASCII letters, digits, underscores, and dollar signs. A nonempty prefix must start with a letter or underscore. A suffix can start with a digit.
  • Values cannot contain $$ or the reserved names _INTERNAL_MODEL_IDENTITY and _INTERNAL_DEPLOY_STATE.
  • Each complete object name, including its prefix and suffix, must fit within Snowflake’s 255-character limit.

Continue to use the original output names in deployment.overrides, without the configured prefix or suffix. For example, an override for TRANSACTION still names TRANSACTION, not PRJ123_TRANSACTION_DEV.

Choose the schemas, prefix, and suffix before the first deployment. PyRel uses these settings to identify your model. As long as your model remains the same, the generated schema stays stable so others in your organization can depend on it. If you change them, the next deployment targets a different location and leaves the previous deployment in place. To remove a deployment, run teardown with that deployment’s settings.

The oplog section controls whether rai models deploy records your model’s change history to its op log. In PyRel 1.28 and later, oplog.enabled defaults to true, so deploys record history without any additional configuration. Set it to false only when you want deploys to install your model without recording history. The shared-model synchronization workflow is unavailable while recording is disabled because it relies on the op log.

  1. Set oplog.enabled to false in raiconfig.yaml

    oplog is a top-level section, alongside connections, model, and deployment.

    connections:
    # ...
    oplog:
    enabled: false
  2. Inspect the parsed config

    from relationalai.semantics import Model
    m = Model("MyModel")
    print(m.config.oplog.enabled)
    Output
    False

    This confirms that PyRel loaded the explicit oplog.enabled: false override from your config.

The rai models commands (deploy and pull) read oplog.enabled from the raiconfig.yaml they discover when the command runs. Set it in the config file for those command-line workflows.

To restore the default recording behavior, remove the enabled: false override or set oplog.enabled to true. If you enable recording programmatically, Model.deploy() needs a source file to record, so set model.path in the same config.

Use a standard op log table for a transient deployment target

Section titled “Use a standard op log table for a transient deployment target”

Snowflake can’t create the default Hybrid op log table inside a transient database or schema. If the deployment target is a transient database or schema, set oplog.table_type to standard before deploying:

deployment:
public_schema: MY_DATABASE.MY_TRANSIENT_SCHEMA
identity:
internal_schema: MY_DATABASE.MY_TRANSIENT_SCHEMA_META
oplog:
table_type: standard

For a permanent deployment target, leave out this override to keep the default Hybrid op log table. The override doesn’t change which source tables PyRel supports.

A deployed model keeps its outputs up to date by refreshing them on a schedule. A schedule is a named refresh cadence: for each schedule you define, PyRel creates a Snowflake task that refreshes the outputs assigned to it. Every output must be assigned to a schedule.

Use this table to find the part of scheduling you want to configure:

What to doWhen to do it
Define a scheduleSetting up any deployed model; every model needs at least one schedule.
Assign outputs to schedulesSome outputs need to refresh more or less often than others.
Skip the deploy-time refreshAn external pipeline or manual trigger drives the first refresh.
Auto-suspend test schedulesDeveloping a model, to keep test deployments from refreshing indefinitely.

Define one or more named schedules under deployment.schedules, then set a default schedule for all outputs with deployment.schedule:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30 # refresh outputs every 30s
schedule: standard # default schedule for all outputs

interval_s is how often the schedule’s task refreshes its outputs, in seconds. It must be 0 or at least 10. Use 0 for a schedule whose refresh you trigger manually or from an external pipeline instead of on a fixed interval.

Schedule names must start with a letter and contain only letters, numbers, and underscores.

Every output refreshes on exactly one schedule. Set the default schedule for all outputs with deployment.schedule, then assign individual outputs to a different schedule with deployment.overrides. Each override lists the objects it applies to and the schedule they refresh on:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 300 # most outputs refresh every 5 minutes
fast_path:
interval_s: 30 # these outputs refresh every 30s
schedule: standard # default schedule for all outputs
overrides:
- objects:
- ACCOUNT_SCORE
- RISK_FLAG
schedule: fast_path # refresh these two outputs on fast_path instead

Outputs you do not name in an override use the default deployment.schedule. Assign outputs to different schedules to refresh different parts of the model at different rates.

By default, deploying a model triggers each refresh task once. rai models deploy waits for the initial refresh by default. Use --no-wait to return as soon as deployment completes, without waiting for the initial refresh to finish. In PyRel versions before 1.37.0, add --wait to wait for the initial refresh. This initial refresh runs even for a manual schedule with interval_s: 0.

Set run_on_deploy: false on a schedule to skip that initial refresh. Use this when something other than the deploy drives the first refresh, such as an external pipeline that triggers the schedule’s task on its own timing:

schedules:
external_pipeline:
interval_s: 0
run_on_deploy: false # do not refresh at deploy time; the pipeline triggers it
schedule: external_pipeline

To trigger the refresh yourself, run the schedule’s Snowflake task. PyRel creates one task per schedule, named REFRESH_<SCHEDULE> (with the schedule name in upper case) in the model’s internal schema. For the external_pipeline schedule above, deployed to MY_DATABASE.MY_MODEL, you would run:

EXECUTE TASK MY_DATABASE.MY_MODEL_META.REFRESH_EXTERNAL_PIPELINE;

Running the task requires the account-level EXECUTE TASK privilege.

During development, set suspend_after_mins to auto-suspend a model’s refresh tasks after a period, so lingering tasks from test deployments do not keep running:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
suspend_after_mins: 15 # suspend refresh tasks 15 minutes after the deploy finishes
schedules:
standard:
interval_s: 30
schedule: standard

Leave suspend_after_mins unset in production, where you want outputs to keep refreshing.

Configure how PyRel creates and manages deployed outputs in the deployment section. Set shared materialization and schedule defaults directly under deployment, and use deployment.overrides for named outputs that need different settings or direct row access, projection, or masking policies.

Every output is a view in the public schema, whatever you set here. These two settings, deployment.materialized and deployment.incremental, decide how PyRel stores the data that view reads from, in the internal schema. Both accept auto (the default), true, or false, and you can override either one for individual outputs with deployment.overrides.

  • materialized: whether the data behind the output is stored. true stores it in a table, false leaves the view to compute it on read, and auto lets the compiler decide.
  • incremental: whether PyRel maintains that stored data incrementally. true uses a Snowflake dynamic table. auto currently behaves like false, so PyRel refreshes the data itself.

Setting incremental: true together with materialized: false is a configuration error, because there is no stored data to maintain.

Because the public object is always a view, these settings change query performance and refresh behavior, not the shape of your SQL interface. A query against an output keeps working whichever way you set them.

For example, this configuration lets PyRel choose for most outputs and maintains ACCOUNT_SCORE incrementally:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
schedule: standard
materialized: auto # let PyRel choose for each output
overrides:
- objects:
- ACCOUNT_SCORE
incremental: true # back this one with a dynamic table

Name outputs the way you query them, without any configured prefix or suffix, which PyRel adds for you. You can also name a single column as OUTPUT.COLUMN, which applies the setting only to the objects behind that column and wins over a setting on the whole output. Internal object names match nothing: if an override matches no output, PyRel warns and ignores it.

Configure row access, projection, or masking policies to control access to deployed outputs. PyRel attaches existing Snowflake policies to the managed outputs without creating or changing their definitions. Policies attach to the public views only, never to the objects storing their data, so readers go through the view and the policy applies.

Before you configure the outputs:

  • Your Snowflake account must use Enterprise Edition or higher.
  • Each policy must already exist. Creating a projection or masking policy requires the corresponding CREATE PROJECTION POLICY or CREATE MASKING POLICY privilege on its schema; attaching an existing policy does not require either creation privilege.
  • The role set by deployment.role needs USAGE or another sufficient privilege on the policy’s database and schema.
  • The role set by deployment.role must have its usual deployment privileges and either APPLY on each policy or the corresponding account-level privilege: APPLY ROW ACCESS POLICY, APPLY PROJECTION POLICY, or APPLY MASKING POLICY.
  • When using policy-level APPLY for a projection or masking policy, the role must also own the target output. See Snowflake’s projection-policy privileges and masking-policy privileges.

PyRel uses the deployment role for deployment and refresh queries. The deployment role must be able to read all rows and original column values, including projecting protected columns. Policies that restrict this role can affect deployment and refresh results.

Configure policies under deployment.overrides[].policies in your existing raiconfig.yaml. Use an override for every output that needs a direct policy. The policy applies to every output of the override group. Each of those outputs must contain all columns referenced by those policies in the configuration.

You cannot set a default direct access policy applying to all possible outputs. To manage default policies, use Snowflake tag-based policies. Assign the policies to tags in Snowflake, then apply those tags to the deployed objects with deployment.tags.

After deployment, check policy bindings and results as consumer roles.

Apply a row access policy to deployed outputs
Section titled “Apply a row access policy to deployed outputs”

Use a Snowflake row access policy to control which rows different users or roles can see in a deployed output.

Add row_access_policy under the output override’s policies in your existing raiconfig.yaml. This example assumes your model emits an ORDERS output with a TENANT_ID column:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
schedule: standard
overrides:
- objects:
- ORDERS
policies:
row_access_policy:
fqn: GOVERNANCE.POLICIES.TENANT_ACCESS
columns:
- TENANT_ID

The columns field is a non-empty, ordered list of output columns. Its order and the corresponding output data types must match the arguments in the policy signature.

Apply a projection policy to output columns
Section titled “Apply a projection policy to output columns”

Use a Snowflake projection policy to restrict whether a column’s values appear in query results while still allowing users to filter and join on that column. The policy controls whether the column can appear in those results based on the query’s context, such as the active role.

Add projection_policies under the output override’s policies in your existing raiconfig.yaml. This example assumes your model emits a CUSTOMERS output with EMAIL and PHONE columns:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
schedule: standard
overrides:
- objects:
- CUSTOMERS
policies:
projection_policies:
- fqn: GOVERNANCE.POLICIES.HIDE_IDENTIFIERS
columns:
- EMAIL
- PHONE

Each entry binds the existing policy identified by its fully qualified name fqn independently to each column in the non-empty columns list. These columns are policy targets, not arguments passed to the policy.

Use a Snowflake masking policy to replace sensitive column values at query time according to the policy’s conditions.

Add masking_policies under the output override’s policies in your existing raiconfig.yaml. This example assumes your model emits a CUSTOMERS output with an EMAIL column whose data type matches the policy’s input and return type:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
schedule: standard
overrides:
- objects:
- CUSTOMERS
policies:
masking_policies:
- fqn: GOVERNANCE.POLICIES.EMAIL_MASK
columns:
- EMAIL

Each entry binds the existing policy identified by its fully qualified name fqn independently to every column in its non-empty columns list.

For a conditional masking policy, which also uses other columns to decide what to return, add conditional_columns to the masking policy entry. For example, replace the entry above with this binding when the existing policy accepts an email value followed by visibility and region values:

- fqn: GOVERNANCE.POLICIES.CONDITIONAL_EMAIL_MASK
columns:
- EMAIL
conditional_columns:
- VISIBILITY
- REGION

If you list multiple targets in columns, each gets a separate binding with that target as the first argument, followed by the same ordered conditional_columns. Use separate entries when targets need different policies or conditional columns. All columns must exist in the output, and each binding’s argument order and data types must match the policy signature. Do not include any target from columns in conditional_columns. Omit conditional_columns for a policy that takes only the protected value.

You can put row_access_policy, projection_policies, and masking_policies in the same override’s policies block.

PyRel checks these restrictions before deployment:

  • A column can have at most one direct projection policy and one direct masking policy. Both can protect the same column.
  • A projection policy’s target column cannot also be a row access policy argument or a masking policy’s conditional column.
  • A row access policy argument cannot also be a masking policy’s conditional column.

Some combinations require checks by Snowflake. For example, using a masked column as a row access policy argument or another masking policy’s conditional argument requires EXEMPT_OTHER_POLICIES = TRUE on the masking policy protecting that column and compatible policy-owner roles. PyRel does not configure that setting or fully validate external policy definitions. If Snowflake rejects an assignment, have the policy owner correct its signature, privileges, or compatibility, then redeploy.

Save the configuration and deploy the model:

Terminal window
rai models deploy

After deployment and a refresh finish, check representative rows and confirm that the policy allows the deployment role to read the full row set and original values. If your schedule has run_on_deploy: false, trigger a refresh before checking the data. Use a Snowflake user who can switch to the deployment role, a role your policy should allow, and a role it should restrict. Each role needs SELECT on the output and USAGE on its database, schema, and query warehouse. For the CUSTOMERS examples above, compare what each role can select:

USE ROLE MY_DEPLOYMENT_ROLE;
SELECT EMAIL FROM MY_DATABASE.MY_MODEL.CUSTOMERS LIMIT 10;
USE ROLE MY_ALLOWED_CONSUMER;
SELECT EMAIL FROM MY_DATABASE.MY_MODEL.CUSTOMERS LIMIT 10;
USE ROLE MY_RESTRICTED_CONSUMER;
SELECT EMAIL FROM MY_DATABASE.MY_MODEL.CUSTOMERS LIMIT 10;

For projection, the restricted query may fail or return NULL, depending on the policy’s enforcement. For masking, compare the returned value with what the policy should return for that role. To compare the same rows, filter by an unprotected stable key in your output; LIMIT alone does not guarantee the same rows. Switching roles alone does not guarantee different results. If a policy restricts the deployment role, have the policy owner allow it to read all rows and original values, then redeploy before relying on refreshes. An appropriately privileged administrator can also inspect Snowflake policy references to check the bindings; ordinary SELECT on the output does not grant that permission.

When PyRel proceeds with a deployment, it checks the visibility and kind of each configured row access, projection, and masking policy before creating or updating deployed outputs. If a policy is missing or unavailable to the deploying role, deployment stops with Invalid Access Policies and lists each unavailable policy with its kind, fully qualified name, and Snowflake error. Check the configured fqn and policy kind, then have your Snowflake administrator confirm that the existing policy is accessible to deployment.role and that the role has the applicable privileges above. Correct the configuration or permissions and retry rai models deploy; after it finishes, check the bindings and query again as the intended reader roles.

This preflight checks that Snowflake can describe the policies. It does not prove that their signatures match the output columns or that every reader sees the intended results. Dry runs skip remote policy validation. If the deployed model already matches the current model and configuration and is ready, PyRel can return without redeploying or rechecking the policies.

Use deployment.tags to put your own Snowflake object tags on every object a deploy creates. That covers the model’s output views, along with the internal objects PyRel manages for the deployment, such as the objects storing the outputs’ data, refresh tasks, procedures, and the metadata tables in the internal schema. Use it for cost attribution, data classification, or tag-based policies that Snowflake applies to any object carrying a given tag.

Before you configure tags:

  • Each tag must already exist in Snowflake.
  • The role set by deployment.role must hold the APPLY privilege on each tag, and USAGE on the database and schema that contain it.

Add your tags under deployment.tags in your existing raiconfig.yaml. Each key is a fully qualified tag name in the form <DATABASE>.<SCHEMA>.<TAG>, and each value is the string assigned to that tag:

deployment:
public_schema: MY_DATABASE.MY_MODEL
identity:
internal_schema: MY_DATABASE.MY_MODEL_META
role: MY_DEPLOYMENT_ROLE
tags:
GOVERNANCE.TAGS.COST_CENTER: engineering
GOVERNANCE.TAGS.DATA_CLASSIFICATION: internal

A value must be at most 256 characters, and it must be one of the tag’s ALLOWED_VALUES when the tag defines them.

The tags are applied to all deployed objects in the public and internal schemas. They are not applied to the schemas themselves, as Snowflake propagates a schema’s tags to every object it contains, including objects the model does not own.

After the deploy finishes, confirm the tags landed on an object by querying Snowflake’s TAG_REFERENCES.

Set deployment.preserve_column_case: true to keep the capitalization of model field names in Snowflake columns. The default is false, so column names follow Snowflake’s case-insensitive rules for unquoted identifiers. For example, a preserved EntityId column must be written as "EntityId" in SQL. This setting does not affect table names.

Use reasoner settings to control which logic, prescriptive, and predictive reasoners PyRel uses for your workloads, and how it waits for results. This guide covers reasoner selection, reasoner-specific settings, and polling behavior.

Understand what reasoner configuration covers

Section titled “Understand what reasoner configuration covers”

These settings apply after PyRel submits work and a reasoner runs your job:

1. Load and validateconfiguration2. Build modeland query3. SubmitRelationalAI job4. Run job withreasoners5. Materializeresults

Use the sections in this guide to tune five parts of that step:

Each section shows the raiconfig.yaml form, the programmatic form, and a quick way to verify the setting. If your model runs graph algorithms, also read Deploy a model that uses graph reasoning.

Use reasoner name and size settings to configure specific reasoners for PyRel to use.

There are a couple of common patterns for using these settings. Use the following table to choose the workflow that matches your use case:

WorkflowUse it when
Create a new reasoner and use itYou have no existing reasoners and need to fully manage them from config.
Use a reasoner that already existsYou already have a named reasoner and want PyRel to use it as-is.

The following examples show both the raiconfig.yaml and programmatic config patterns for each workflow.

Use this workflow when you have no existing reasoners and want to manage them from your config.

If you want to create the logic reasoner yourself first, use the CLI:

Terminal window
rai reasoners create --type logic --name team_logic --size HIGHMEM_X64_S --wait

Then configure PyRel to use that reasoner name and size:

connections:
# ...
reasoners:
logic:
name: team_logic
size: HIGHMEM_X64_S
  • If name and size are configured and the named reasoner does not exist, PyRel creates it using the configured size.
  • Predictive reasoners are created from config the first time PyRel needs them.
  • If the named reasoner already exists, PyRel uses it as-is and does not resize it from config.

Use this workflow when the named reasoner already exists and you want PyRel to use it.

Set reasoners.logic.name to the existing logic reasoner name:

connections:
# ...
reasoners:
logic:
name: team_logic
  • reasoners.logic.name tells PyRel which existing logic reasoner to use for model work.
  • The example omits size because the reasoner already exists.
  • PyRel uses the named reasoner as-is and does not change its size from config.
  • If the reasoner is ever deleted, PyRel will create a new one with the same name if necessary, but it will use the default size for that reasoner type instead of the old size.
  • For logic, prescriptive, and predictive reasoners, the default size is HIGHMEM_X64_S.
  • If you omit reasoners.predictive.name, PyRel defaults the predictive reasoner name to your Snowflake username with dots replaced by underscores.

Logic reasoners run in app-managed compute pools. You do not create or manage these pools directly, but you do choose logic reasoner sizes that map to them.

Compute poolSizeSupported cloud platformsUsed for
RELATIONAL_AI_HIGHMEM_X64_SHIGHMEM_X64_SAWS, AzureSmall logic workloads and the default logic reasoner size.
RELATIONAL_AI_HIGHMEM_X64_MHIGHMEM_X64_MAWS, AzureMedium logic workloads.
RELATIONAL_AI_HIGHMEM_X64_LHIGHMEM_X64_LAWS onlyLarge logic workloads on AWS.
RELATIONAL_AI_HIGHMEM_X64_SLHIGHMEM_X64_SLAzure onlyLarge logic workloads on Azure.

PyRel creates logic reasoners automatically. Users can configure the name and size of the logic reasoner that PyRel creates for them.

When a user runs a query that needs a logic reasoner:

  • PyRel checks whether the configured reasoner exists and is READY.
  • If it is READY, PyRel sends the query to that reasoner.
  • If it is not READY, PyRel resumes it and waits for it to become READY.
  • If it does not exist, PyRel creates it using the configured size, or HIGHMEM_X64_S if no size is configured.

The default logic reasoner settings are:

SettingBehavior
NameDefaults to the Snowflake username with dots replaced by underscores.
SizeDefaults to HIGHMEM_X64_S.
Auto-suspendDefaults to 60 minutes of inactivity for newly created reasoners when you do not set a different value.

The logic reasoner runs the rules-based parts of your semantic model. PyRel uses it when it evaluates definitions, requirements, and queries. It also runs the graph algorithms listed in Which graph algorithms use the logic reasoner.

The settings in this section live under reasoners.logic.*. Most users can leave them at their defaults. If you do need to change a setting, use this table to find the right subsection:

SubsectionUse it when
Constraint emissionYou want more detail about requirement failures for debugging.
Incremental maintenanceYou want to tune repeated runs that process small data changes.

Use emit_constraints when you need more detail while debugging a failed require() check. This setting controls whether PyRel asks the logic reasoner for additional diagnostics when a requirement fails.

Requirements still pass or fail the same way whether this setting is on or off. It defaults to false, so PyRel returns its usual failure information unless you enable it. When extra diagnostics are available, they may include backend-dependent details such as the violated requirement text or the entities involved in the failing case.

Select the tab for your preferred configuration method and follow the steps to set emit_constraints and verify it.

  1. Set reasoners.logic.emit_constraints in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    logic:
    emit_constraints: true
  2. Verify the configured value:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured value.
    print(m.config.reasoners.logic.emit_constraints)

Use reasoners.logic.incremental_maintenance to control whether the logic reasoner reuses work from an earlier run when you rerun the same query after your source data changes. This matters most when you regularly rerun the same query or regenerate the same report on refreshed data, such as rerunning the same weekly report after the underlying source tables update.

Leave this setting at "off" unless that repeated-query pattern is important for your workload. "off" is the default and the safest place to start.

Choose one of these modes:

  • "off" (default): Do not use incremental updates.
  • "auto": Use incremental updates where they are supported.
  • "all": Try incremental updates more broadly than "auto".

Select the tab for your preferred configuration method and follow the steps to set incremental_maintenance and verify it.

  1. Set reasoners.logic.incremental_maintenance in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    logic:
    incremental_maintenance: "auto"
  2. Verify the configured value:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured value.
    print(m.config.reasoners.logic.incremental_maintenance)

A model that builds a graph and runs graph algorithms deploys with the same configuration and the same rai models deploy command as any other model. Three things are specific to graphs:

  • The graph’s node and edge concepts each become a view in the public schema, like any other concept. See Understand the deployed representation.
  • An algorithm result is computed on every refresh, but it stays in the internal schema unless your model exposes it. See Expose algorithm results as outputs.
  • Some algorithms run on the logic reasoner rather than in SQL, so the refresh creates or resumes that reasoner when it needs it. No reasoner setting is required.

Which graph algorithms use the logic reasoner

Section titled “Which graph algorithms use the logic reasoner”

A deployed model that runs graph algorithms needs no reasoner setting. When a refresh needs the logic reasoner, it resumes or creates the configured one, as described in How logic reasoners are created.

The following algorithms run on the logic reasoner:

Every other algorithm runs in SQL.

  • This list describes current behavior, not a contract. Later releases may compute more algorithms in SQL.
  • The first refresh after you add one of these algorithms can take longer while the reasoner starts. Later refreshes queue a job on it.

A graph result stays in your internal schema until it becomes a part of your ontology. It becomes part of your ontology in one of two ways: you attach it to the node concept, or you use it in a definition. This example does both for a payment graph:

from relationalai.semantics import Integer, Model
from relationalai.semantics.reasoners.graph import Graph
m = Model("AccountInfluence")
Account = m.Concept("Account", identify_by={"id": Integer})
Payment = m.Concept("Payment", identify_by={"id": Integer})
Payment.payer = m.Property(f"{Payment} sent from {Account:payer}")
Payment.payee = m.Property(f"{Payment} sent to {Account:payee}")
Account.counterparties = m.Property(f"{Account} has {Integer:counterparties} counterparties")
16 collapsed lines
account_data = m.data([{"id": 1}, {"id": 2}, {"id": 3}, {"id": 4}])
m.define(Account.new(account_data.to_schema()))
payment_data = m.data([
{"id": 100, "payer_id": 1, "payee_id": 2},
{"id": 101, "payer_id": 2, "payee_id": 3},
{"id": 102, "payer_id": 3, "payee_id": 1},
{"id": 103, "payer_id": 1, "payee_id": 4},
])
m.define(
Payment.new(
id=payment_data.id,
payer=Account.lookup(id=payment_data.payer_id),
payee=Account.lookup(id=payment_data.payee_id),
)
)
graph = Graph(m, directed=True, weighted=False, aggregator="sum", node_concept=Account)
m.define(graph.Edge.new(id=Payment.id, src=Payment.payer, dst=Payment.payee))
# Attach the result to the node concept.
Account.pagerank = graph.pagerank()
# Use the result in a definition.
degree = graph.degree()
m.where(degree(Account, Integer)).define(Account.counterparties(Integer))
  • node_concept=Account makes every account a node, so the graph reuses the ACCOUNT view instead of creating one.
  • Account.pagerank = graph.pagerank() attaches the PageRank result to the node concept. After deployment it’s the PAGERANK column of ACCOUNT.
  • The definition at the end fills the counterparties property from graph.degree(). After deployment it’s the COUNTERPARTIES column of ACCOUNT.
  • A result that you call but never attach or use, such as graph.indegree() on its own line, is still computed and stored, but only in the internal schema.

Use this table to see where each kind of result lands after rai models deploy and the first refresh:

What you writeWhat appearsWhere
Account.pagerank = graph.pagerank()A PAGERANK columnThe ACCOUNT view in the public schema
A definition that fills a property, such as Account.counterpartiesA COUNTERPARTIES columnThe ACCOUNT view in the public schema
A definition that fills a standalone relationship with a short_nameA view named after the short_nameThe public schema
A result you never attach or useAn object with the _INTERNAL_STORE_ prefixThe internal schema only

After the refresh finishes, query the concept view to confirm both columns are populated:

SELECT ID, PAGERANK, COUNTERPARTIES
FROM MY_DATABASE.MY_MODEL.ACCOUNT
ORDER BY ID;

You should see one row per account. PAGERANK holds a score for every account. COUNTERPARTIES holds the number of other accounts each account exchanges payments with: 3, 2, 2, and 1 for accounts 1 through 4. If a column is missing, see A graph algorithm result does not appear in the public schema. If the columns exist but are empty, the refresh hasn’t finished yet. See Monitor a deployed model.

Which compute pools host prescriptive reasoners

Section titled “Which compute pools host prescriptive reasoners”

Prescriptive reasoners run in app-managed compute pools. You do not create or manage these pools directly, but you do choose prescriptive reasoner sizes that map to them.

Compute poolSizeSupported cloud platformsUsed for
RELATIONAL_AI_HIGHMEM_X64_SHIGHMEM_X64_SAWS, AzureSmall prescriptive workloads and the default prescriptive reasoner size.
RELATIONAL_AI_HIGHMEM_X64_MHIGHMEM_X64_MAWS, AzureMedium prescriptive workloads.

PyRel creates prescriptive reasoners automatically. Users can configure the name and size of the prescriptive reasoner that PyRel creates for them.

When a user runs a prescriptive workflow that needs a prescriptive reasoner:

  • PyRel checks whether the configured reasoner exists and is READY.
  • If it is READY, PyRel sends the solve workload to that reasoner.
  • If it exists but is not READY, PyRel resumes it and waits for it to become READY.
  • If it does not exist, PyRel creates it using the configured size, or HIGHMEM_X64_S if no size is configured.

The default prescriptive reasoner settings are:

SettingBehavior
SizeDefaults to HIGHMEM_X64_S when PyRel creates a prescriptive reasoner and you do not set a different value.
Auto-suspendDefaults to 60 minutes of inactivity for newly created reasoners when you do not set a different value.

Prescriptive reasoners run decision-problem and optimization workloads. Use reasoners.prescriptive.* when you want PyRel to select a specific prescriptive reasoner or apply a prescriptive query timeout. Older docs may still call the same workload family solver reasoners, but the public config key is prescriptive.

Most users only need to choose the reasoner identity or set a timeout. Use this table to choose the right subsection:

SubsectionUse it when
Name and sizeYou want PyRel to create or reuse a specific prescriptive reasoner.
Query timeoutYou want prescriptive queries to stop after an explicit time limit.

Set the prescriptive reasoner name and size

Section titled “Set the prescriptive reasoner name and size”

Use reasoners.prescriptive.name and reasoners.prescriptive.size when you want PyRel to create a specific prescriptive reasoner or bind to one that already exists. Set both fields when you want PyRel to create the reasoner if it is missing.

Select the tab for your preferred configuration method and follow the steps to set the name, size, and verify the configured values.

  1. Set reasoners.prescriptive.name and reasoners.prescriptive.size in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    prescriptive:
    name: team_prescriptive
    size: HIGHMEM_X64_S
  2. Verify the configured values:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured values.
    print(m.config.reasoners.prescriptive.name)
    print(m.config.reasoners.prescriptive.size)
  • prescriptive is the public config term, even if older docs say solver.
  • HIGHMEM_X64_S is the default prescriptive size. HIGHMEM_X64_M is also supported for prescriptive workloads.
  • If the named prescriptive reasoner already exists, PyRel uses it as-is and does not resize it from config.

Set a query timeout for prescriptive workloads

Section titled “Set a query timeout for prescriptive workloads”

Use reasoners.prescriptive.query_timeout_mins when you want an explicit limit for prescriptive queries. Set it when you want PyRel to stop waiting for a long-running prescriptive workload after a fixed number of minutes.

Select the tab for your preferred configuration method and follow the steps to set query_timeout_mins and verify it.

  1. Set reasoners.prescriptive.query_timeout_mins in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    prescriptive:
    query_timeout_mins: 30
  2. Verify the configured value:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured value.
    print(m.config.reasoners.prescriptive.query_timeout_mins)

Which compute pools host predictive reasoners

Section titled “Which compute pools host predictive reasoners”

Predictive reasoners run in app-managed compute pools. You do not create or manage these pools directly, but you do choose predictive reasoner sizes that map to them.

Compute poolSizeSupported cloud platformsUsed for
RELATIONAL_AI_HIGHMEM_X64_SHIGHMEM_X64_SAWS, AzureSmall predictive workloads and the default predictive reasoner size.
RELATIONAL_AI_HIGHMEM_X64_MHIGHMEM_X64_MAWS, AzureMedium predictive workloads.
RELATIONAL_AI_HIGHMEM_X64_LHIGHMEM_X64_LAWS onlyLarge CPU-based predictive workloads on AWS.
RELATIONAL_AI_GPU_NV_SGPU_NV_SAWS onlyGPU-backed predictive workloads on AWS.
RELATIONAL_AI_GPU_NV_SMGPU_NV_SMAzure onlyGPU-backed predictive workloads on Azure.

The Native App also manages internal-only pools such as RELATIONAL_AI_ERP_COMPUTE_POOL and RELATIONAL_AI_COMPILE_CACHE_SPCS. Those pools support app internals, not user-managed predictive reasoners.

PyRel creates predictive reasoners automatically. Users can configure the name and size of the predictive reasoner that PyRel creates for them.

When a user runs a predictive workflow that needs a predictive reasoner:

  • PyRel checks whether the configured reasoner exists and is READY.
  • If it is READY, PyRel sends the predictive workload to that reasoner.
  • If it exists but is not READY, PyRel resumes it and waits for it to become READY.
  • If it does not exist, PyRel creates it using the configured size, or HIGHMEM_X64_S if no size is configured.

The default predictive reasoner settings are:

SettingBehavior
NameDefaults to the Snowflake username with dots replaced by underscores.
SizeDefaults to HIGHMEM_X64_S.
Auto-suspendDefaults to 60 minutes of inactivity for newly created reasoners when you do not set a different value.

Predictive reasoners run the compute behind PyRel predictive reasoning workflows. Use reasoners.predictive.* when you want PyRel to select a specific predictive reasoner or apply a predictive query timeout. This section covers reasoner-instance configuration only, not GNN feature, training, or model-architecture settings.

Most users only need to choose the reasoner identity or set a timeout. Use this table to choose the right subsection:

SubsectionUse it when
Name and sizeYou want PyRel to create or reuse a specific predictive reasoner.
Query timeoutYou want predictive queries to stop after an explicit time limit.

Use reasoners.predictive.name and reasoners.predictive.size when you want PyRel to create a specific predictive reasoner or bind to one that already exists. Set both fields when you want PyRel to create the reasoner if it is missing.

Select the tab for your preferred configuration method and follow the steps to set the name, size, and verify the configured values.

  1. Set reasoners.predictive.name and reasoners.predictive.size in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    predictive:
    name: team_predictive
    size: HIGHMEM_X64_M
  2. Verify the configured values:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured values.
    print(m.config.reasoners.predictive.name)
    print(m.config.reasoners.predictive.size)
  • If you omit reasoners.predictive.name, PyRel defaults the predictive reasoner name to your Snowflake username with dots replaced by underscores.
  • HIGHMEM_X64_S is the default predictive size. HIGHMEM_X64_M is supported on AWS and Azure. HIGHMEM_X64_L and GPU_NV_S are AWS-only. GPU_NV_SM is Azure-only.
  • Predictive reasoners are created from config the first time a predictive workflow needs them.

Set a query timeout for predictive workloads

Section titled “Set a query timeout for predictive workloads”

Use reasoners.predictive.query_timeout_mins when you want an explicit limit for predictive queries. Set it when you want PyRel to stop waiting for a long-running predictive workload after a fixed number of minutes.

Select the tab for your preferred configuration method and follow the steps to set query_timeout_mins and verify it.

  1. Set reasoners.predictive.query_timeout_mins in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    predictive:
    query_timeout_mins: 45
  2. Verify the configured value:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the configured value.
    print(m.config.reasoners.predictive.query_timeout_mins)

Polling settings control how often PyRel checks the status of a long-running reasoner operation. Most users should keep the defaults. Change them only if the default wait behavior is already a problem, for example if status updates feel too slow or you need to reduce polling noise.

Select the tab for your preferred configuration method and follow the steps to set the polling settings and verify them.

  1. Set polling settings under reasoners in raiconfig.yaml:

    connections:
    # ...
    reasoners:
    poll_initial_delay_s: 0.5
    poll_overhead_rate: 0.2
    poll_max_delay_s: 2.0
  2. Verify the configured values:

    from relationalai.config import create_config
    from relationalai.semantics import Model
    cfg = create_config()
    # Use the config.
    m = Model("MyModel", config=cfg)
    # Verify the polling settings.
    print(m.config.reasoners.poll_initial_delay_s)
    print(m.config.reasoners.poll_overhead_rate)
    print(m.config.reasoners.poll_max_delay_s)