Skip to content

Configure a model

Model configuration controls how PyRel loads your model, how it treats properties you have not declared, and how the model deploys its outputs to Snowflake. These settings live in three sections of your configuration: model for how the model runs, deployment for where and how it deploys to Snowflake, and oplog for whether deploys record change history. This guide shows how to set them, and how to inspect the values PyRel actually used.

  • You have access to a Snowflake account with the RelationalAI Native App installed. If you are unsure, contact your Snowflake administrator.
  • You have a working PyRel installation. See Start building with PyRel for instructions.

Use this table to find the setting for what you want to change:

SettingUse it when
model.implicit_propertiesYou want accessing an undeclared property to raise an error instead of silently creating it, for example before deploying to production.
model.pathYou are deploying a model and want to point PyRel at its entry point script or directory.
deployment.schemaYou want to choose the Snowflake schema and role a model deploys to.
deployment.schedulesYou want to control when a deployed model’s outputs refresh after deployment.
deployment.outputsYou want to choose Snowflake object types or configure access policies for deployed outputs.
deployment.tagsYou want your own Snowflake object tags on everything a deploy creates, for cost attribution, classification, or tag-based policies.
deployment.preserve_column_caseYou want deployed Snowflake columns to preserve the capitalization of model field names.
oplog.enabledYou want to override or explicitly declare whether rai models deploy records change history for the branch, pull, and merge workflows.

By default, PyRel creates a property the first time you access it, even if you never declared it. Set the model.implicit_properties configuration option to false to require every property to be declared before use, so that accessing an undeclared property raises an error.

  1. Set model.implicit_properties in raiconfig.yaml

    connections:
    # ...
    model:
    implicit_properties: false
  2. Inspect the parsed config

    from relationalai.semantics import Model
    m = Model("MyModel")
    print(m.config.model.implicit_properties)

    A printed value of False confirms that PyRel loaded the implicit_properties setting from your config.

model.path points PyRel at the script or directory that defines your model, for example model.py. It has no default (None).

Deployment and related tooling use model.path to locate your model. For example, rai models deploy deploys the model at model.path when you do not pass an explicit --path option to the command.

  1. Set model.path in raiconfig.yaml

    connections:
    # ...
    model:
    path: model.py
  2. Inspect the parsed config

    from relationalai.semantics import Model
    m = Model("MyModel")
    print(m.config.model.path)

    A printed value of model.py confirms that PyRel loaded the path setting from your config.

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

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

The meta_schema and auto_deploy settings are optional.

Here is what each setting does:

  • schema: The fully-qualified Snowflake schema (<DATABASE>.<SCHEMA>) that holds the model’s output tables and views. 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.
  • meta_schema: The schema where PyRel stores the metadata it uses to manage the deployment, such as stored procedures, the refresh plan table, and the refresh log. Keeping it separate from schema prevents PyRel’s bookkeeping from mixing with your model’s outputs. If you omit it, PyRel derives it from schema as <schema>_META, 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.object_name_prefix or deployment.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:
schema: MY_DATABASE.SHARED_MODELS
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.outputs.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. 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 branch, collaborate, and merge workflows are unavailable while recording is disabled because they rely 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, branch, pull, and merge) 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:
schema: MY_DATABASE.MY_TRANSIENT_SCHEMA
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 under deployment.outputs:

deployment:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30 # refresh outputs every 30s
outputs:
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.outputs.schedule, then assign individual outputs to a different schedule with deployment.outputs.overrides. Each override lists the objects it applies to and the schedule they refresh on:

deployment:
schema: MY_DATABASE.MY_MODEL
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
outputs:
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 outputs.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. Use rai models deploy --wait to wait for the initial refresh; without --wait, outputs may still be refreshing when the command returns. 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
outputs:
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 meta 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:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
suspend_after_mins: 15 # suspend refresh tasks 15 minutes after the deploy finishes
schedules:
standard:
interval_s: 30
outputs:
schedule: standard

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

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

By default, PyRel creates each deployed output as a Snowflake dynamic table when it can. You can override the default for all outputs with deployment.outputs.type, or override individual outputs with deployment.outputs.overrides. The valid output types are:

  • dynamic_table
  • table
  • view

For example, this configuration creates all outputs as tables except for ACCOUNT_SCORE, which is a dynamic table:

deployment:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
outputs:
schedule: standard
type: table # default output type
overrides:
- objects:
- ACCOUNT_SCORE
type: dynamic_table # override this output

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.

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.
  • The role managing a dynamic table’s policies must own that dynamic table. See Snowflake’s dynamic-table ownership requirements.

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.outputs.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:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
outputs:
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:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
outputs:
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:
schema: MY_DATABASE.MY_MODEL
role: MY_DEPLOYMENT_ROLE
schedules:
standard:
interval_s: 30
outputs:
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 --wait

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 --wait; 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 tables, views, and dynamic tables, along with the internal objects PyRel manages for the deployment, such as refresh tasks, procedures, and the metadata tables in the meta 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:
schema: MY_DATABASE.MY_MODEL
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 deployment and meta schema. 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.