{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9023f6dd",
   "metadata": {},
   "source": [
    "# RelationalAI Native App Setup Guide\n",
    "\n",
    "> :warning: Note that the `ACCOUNTADMIN` role is used in this guide. This role is needed for Step 1 and for creating the network rule in Step 4. To manage roles specific the RelationalAI Native App, see Appendix 2 at the bottom of this notebook.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f1aedd8d",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "USE ROLE ACCOUNTADMIN;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c696a63",
   "metadata": {},
   "source": [
    "## Step 1 - Activate the RAI Native App\n",
    "\n",
    "Execute the following three cells to create the RAI service (this usually takes between 5 and 15 minutes):\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4089b183",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "import time\n",
    "import json\n",
    "import itertools\n",
    "from snowflake.snowpark.context import get_active_session\n",
    "\n",
    "session = get_active_session()\n",
    "\n",
    "def poll(f):\n",
    "\n",
    "    last_message = \"\"\n",
    "    dots = itertools.cycle([\"⠧\", \"⠏\", \"⠛\", \"⠹\", \"⠼\", \"⠶\"])\n",
    "\n",
    "    def status(message):\n",
    "        spaces = \" \" * (len(\"⠿ \" + last_message) - len(message))\n",
    "        sys.stdout.write(\"\\r\" + message + spaces)\n",
    "        sys.stdout.flush()\n",
    "\n",
    "    for ctr in itertools.count():\n",
    "        if ctr % 10 == 0:\n",
    "            result = f()\n",
    "            if isinstance(result, str):\n",
    "                message = next(dots) + \" \" + result\n",
    "                status(message)\n",
    "                last_message = result\n",
    "            if result is True:\n",
    "                status(\"⠿ Done!\")\n",
    "                return\n",
    "        else:\n",
    "            message = next(dots) + \" \" + last_message\n",
    "            status(message)\n",
    "        time.sleep(0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78cdeba7",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "def activate():\n",
    "    try:\n",
    "        session.sql(\"CALL RELATIONALAI.APP.ACTIVATE();\").collect()\n",
    "        return True\n",
    "    except Exception as e:\n",
    "        if \"Unknown user-defined function\" in str(e):\n",
    "            return \"Waiting for app activation to complete...\"\n",
    "        else:\n",
    "            raise e\n",
    "\n",
    "poll(activate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4872e79",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "def check():\n",
    "    result = session.sql(\"CALL RELATIONALAI.APP.SERVICE_STATUS();\").collect()\n",
    "    status = json.loads(result[0][\"SERVICE_STATUS\"])[0][\"message\"]\n",
    "    if status is None:\n",
    "        status = \"\"\n",
    "    elif status.startswith(\"UNKNOWN\"):\n",
    "        status = \"Working\"\n",
    "    elif status.startswith(\"Readiness probe\"):\n",
    "        status = \"Almost done\"\n",
    "    elif status == \"Running\":\n",
    "        return True\n",
    "    else:\n",
    "        return status + \"...\"\n",
    "\n",
    "poll(check)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9763afd5",
   "metadata": {},
   "source": [
    "> :warning: Note: Please review the [documentation on upgrades](https://docs.relational.ai/manage/app/upgrade) for details about the weekly automatic upgrade schedule.\n",
    "\n",
    "## Step 2 - Setting up Change Data Capture\n",
    "\n",
    "Streams share Snowflake data with the RAI Native App using change data capture (CDC) to capture source table and view changes once every minute.\n",
    "\n",
    "To enable CDC, run the following command:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "68f1bfe2",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "CALL RELATIONALAI.APP.RESUME_CDC();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74b25658",
   "metadata": {},
   "source": [
    "## Step 3 - Creating user roles\n",
    "\n",
    "The RAI Native App comes with a set of Snowflake application roles for managing access to the app. Application roles can't be granted to users directly, and must be granted to Snowflake database roles instead.\n",
    "\n",
    "The following SQL creates two database roles: `rai_admin` and `rai_developer`. The `rai_admin` role is for full admin access to the RAI Native App, while the `rai_developer` role is for developers who need access to the RAI Python API:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8666f9ea",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "-- Create a rai_admin role for full admin access to the RAI Native App.\n",
    "CREATE ROLE rai_admin;\n",
    "-- Link the app's `all_admin` role to the created role.\n",
    "GRANT APPLICATION ROLE relationalai.all_admin TO ROLE rai_admin;\n",
    "\n",
    "-- Create a role for developers who need access to the RAI Python API.\n",
    "CREATE ROLE rai_developer;\n",
    "-- Link the app's `rai_user` role to the created role.\n",
    "GRANT APPLICATION ROLE relationalai.rai_user TO ROLE rai_developer;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ba9c171",
   "metadata": {},
   "source": [
    "Users granted the `rai_developer` role can:\n",
    "\n",
    "- Use the RAI Native App.\n",
    "- Create, use, and delete RAI reasoners.\n",
    "- Create, manage, and delete data streams.\n",
    "- Enable and disable the CDC service.\n",
    "\n",
    "You can grant the `rai_developer` role to users in your Snowflake account using the `GRANT ROLE rai_developer TO USER \"username\";` command. For example, to grant the `rai_developer` role to yourself, run the following cell:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21c7f225",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "# optional: grant the rai_developer role to yourself\n",
    "session = get_active_session()\n",
    "current_user = session.sql(\"SELECT CURRENT_USER() AS USERNAME;\").collect()[0][\"USERNAME\"]\n",
    "\n",
    "session.sql(f'GRANT ROLE rai_developer TO USER \"{current_user}\"').collect()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "226150ab",
   "metadata": {},
   "source": [
    "Our [Simple Start template](https://relational.ai/build/templates/simple-start) uses a table called `RAI_DEMO.SIMPLE_START.CONNECTIONS`. If you want to be able to run that notebook, either grant permissions to the `rai_developer` role or run the following SQL to create the table now. You can clean up this database when you're done with the demo notebook by running `DROP DATABASE RAI_DEMO CASCADE;`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b69546b",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "CREATE DATABASE IF NOT EXISTS RAI_DEMO;\n",
    "CREATE SCHEMA IF NOT EXISTS RAI_DEMO.SIMPLE_START;\n",
    "\n",
    "CREATE OR REPLACE TABLE RAI_DEMO.SIMPLE_START.CONNECTIONS (\n",
    "    STATION_1 INT,\n",
    "    STATION_2 INT\n",
    ");\n",
    "\n",
    "INSERT INTO RAI_DEMO.SIMPLE_START.CONNECTIONS (STATION_1, STATION_2) VALUES\n",
    "(1, 2),\n",
    "(1, 3),\n",
    "(3, 4),\n",
    "(1, 4),\n",
    "(4, 5),\n",
    "(5, 7),\n",
    "(6, 7),\n",
    "(6, 8),\n",
    "(7, 8);\n",
    "\n",
    "GRANT USAGE ON DATABASE RAI_DEMO TO ROLE rai_developer;\n",
    "GRANT USAGE ON SCHEMA RAI_DEMO.SIMPLE_START TO ROLE rai_developer;\n",
    "GRANT SELECT ON TABLE RAI_DEMO.SIMPLE_START.CONNECTIONS TO ROLE rai_developer;\n",
    "ALTER TABLE RAI_DEMO.SIMPLE_START.CONNECTIONS SET CHANGE_TRACKING = TRUE;\n",
    "\n",
    "-- optional: give rai_developer more extensive permissions in the RAI_DEMO database\n",
    "-- this step is necessary for the user to be able to run all the demo notebooks\n",
    "GRANT CREATE SCHEMA ON DATABASE RAI_DEMO TO ROLE rai_developer;\n",
    "GRANT CREATE TABLE ON SCHEMA RAI_DEMO.SIMPLE_START TO ROLE rai_developer;\n",
    "GRANT CREATE TABLE ON FUTURE SCHEMAS IN DATABASE RAI_DEMO TO ROLE rai_developer;\n",
    "GRANT SELECT ON ALL TABLES IN SCHEMA RAI_DEMO.SIMPLE_START TO ROLE rai_developer;\n",
    "GRANT SELECT ON FUTURE TABLES IN DATABASE RAI_DEMO TO ROLE rai_developer;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3b339c1",
   "metadata": {},
   "source": [
    "## Step 4 — Set Up Snowflake Notebooks\n",
    "\n",
    "Snowflake has two notebook experiences: *Notebooks in Workspaces* and *Legacy Notebooks*.\n",
    "We recommend Notebooks in Workspaces for all new work.\n",
    "This step sets up Notebooks in Workspaces, and it also includes setup for Legacy Notebooks in case your account still needs them.\n",
    "\n",
    "> :warning: Note: Snowflake renamed the original notebook experience to *Legacy Notebooks*.\n",
    "> New accounts created after April 2026 can't create Legacy Notebooks, and Snowflake will turn off creation for all other accounts on September 1, 2026.\n",
    "> Use Notebooks in Workspaces for new work.\n",
    "\n",
    "First, give RelationalAI access to Snowflake stage storage.\n",
    "RelationalAI moves some data through *Snowflake stages*, which are Snowflake's built-in storage locations for files.\n",
    "The code below creates an [External Access Integration](https://docs.snowflake.com/en/sql-reference/sql/create-external-access-integration) — the network setup that lets RelationalAI reach those stages.\n",
    "Several RelationalAI features rely on this access, and a Legacy Notebook that runs on a warehouse needs it to return query results.\n",
    "It reads the stage hosts Snowflake lists for your account with `SYSTEM$ALLOWLIST()`, then allows traffic only to those hosts and to no other internet destination.\n",
    "Whether traffic to those hosts stays on a private network depends on your account's Snowflake network settings.\n",
    "\n",
    "Run this code to create the integration:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e268153e",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "session = get_active_session()\n",
    "\n",
    "system_allowlist = session.sql(\"\"\"\n",
    "SELECT value:host AS URL\n",
    "FROM TABLE(FLATTEN(input=>parse_json(SYSTEM$ALLOWLIST())))\n",
    "WHERE value:type = 'STAGE'\n",
    "\"\"\").collect()\n",
    "\n",
    "if system_allowlist:\n",
    "    urls = \", \".join(row.URL.replace('\"', \"'\") for row in system_allowlist)\n",
    "    egress_rule_commands = [\n",
    "        f\"\"\"\n",
    "        CREATE OR REPLACE NETWORK RULE S3_RAI_INTERNAL_BUCKET_EGRESS\n",
    "        MODE = EGRESS\n",
    "        TYPE = HOST_PORT\n",
    "        VALUE_LIST = ({urls});\n",
    "        \"\"\",\n",
    "        \"\"\"\n",
    "        CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION S3_RAI_INTERNAL_BUCKET_EGRESS_INTEGRATION\n",
    "        ALLOWED_NETWORK_RULES = (S3_RAI_INTERNAL_BUCKET_EGRESS)\n",
    "        ENABLED = true;\n",
    "        \"\"\",\n",
    "        \"\"\"\n",
    "        GRANT USAGE ON INTEGRATION S3_RAI_INTERNAL_BUCKET_EGRESS_INTEGRATION TO ROLE rai_developer;\n",
    "        \"\"\"\n",
    "    ]\n",
    "\n",
    "    for command in egress_rule_commands:\n",
    "        session.sql(command).collect()\n",
    "\n",
    "    print(\"Network rule set up successfully.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70b8fb78",
   "metadata": {},
   "source": [
    "### Notebooks in Workspaces\n",
    "\n",
    "A Workspaces notebook uses two Snowflake compute resources:\n",
    "\n",
    "- A *compute pool* runs the notebook's Python code.\n",
    "- A *query warehouse* runs the SQL queries and other Snowflake compute the notebook triggers.\n",
    "\n",
    "Run the following SQL to create a warehouse and compute pool for your notebooks, then give the `rai_developer` role access to both:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6d6c838",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "CREATE WAREHOUSE notebooks_wh;\n",
    "\n",
    "CREATE COMPUTE POOL NOTEBOOK_CPU_XS\n",
    "    MIN_NODES = 1\n",
    "    MAX_NODES = 15\n",
    "    INSTANCE_FAMILY = CPU_X64_XS\n",
    "    AUTO_RESUME = true\n",
    "    AUTO_SUSPEND_SECS = 1800\n",
    "    COMMENT = \"Pool for Snowflake Notebooks in Workspaces\";\n",
    "\n",
    "GRANT USAGE ON WAREHOUSE notebooks_wh TO ROLE rai_developer;\n",
    "GRANT USAGE ON COMPUTE POOL NOTEBOOK_CPU_XS TO ROLE rai_developer;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a325a041",
   "metadata": {},
   "source": [
    "Now anyone with the `rai_developer` role can create a notebook in **Projects > Workspaces**.\n",
    "When they open the notebook, they connect it to a *notebook service* — the container that runs the notebook's Python — and point that service at the `NOTEBOOK_CPU_XS` compute pool.\n",
    "They also select the `rai_developer` role and the `NOTEBOOKS_WH` warehouse in the role and warehouse picker at the top left.\n",
    "\n",
    "The setup worked if `NOTEBOOK_CPU_XS` and `NOTEBOOKS_WH` appear as options and the notebook connects.\n",
    "\n",
    "Workspaces notebooks can install the `relationalai` package from Snowflake's built-in package repository, which is available to all roles by default.\n",
    "You only need extra network setup to install packages from outside Snowflake or to reach other external services.\n",
    "\n",
    "### Legacy Notebooks\n",
    "\n",
    "Legacy Notebooks are Snowflake's classic notebook experience, which you open from **Projects > Notebooks**.\n",
    "Snowflake is retiring Legacy Notebooks, and these instructions will be removed after Snowflake disables Legacy Notebook creation on September 1, 2026.\n",
    "Set one up only if your account already depends on it, and skip this subsection if you use Notebooks in Workspaces.\n",
    "\n",
    "A Legacy Notebook runs on one of two runtimes:\n",
    "\n",
    "- *Container Runtime* runs the notebook's Python on a compute pool, the same way Notebooks in Workspaces do.\n",
    "- *Warehouse Runtime* runs the notebook's Python on a warehouse.\n",
    "\n",
    "Both runtimes store the notebook in a database and need permission to create notebooks, which the SQL below sets up.\n",
    "A Container Runtime notebook runs its Python on the `NOTEBOOK_CPU_XS` compute pool and its SQL on the `NOTEBOOKS_WH` warehouse you created above.\n",
    "It also needs permission to create services and — to install packages from PyPI — its own External Access Integration.\n",
    "A Warehouse Runtime notebook runs on the `NOTEBOOKS_WH` warehouse you created above and relies on the stage integration you created at the start of this step to return query results.\n",
    "\n",
    "If you use Legacy Notebooks, uncomment and run the SQL below:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa472272",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "-- Set up resources for Legacy Notebooks. Uncomment these statements only if you use Legacy Notebooks.\n",
    "\n",
    "-- CREATE DATABASE IF NOT EXISTS rai_notebooks;\n",
    "-- GRANT USAGE ON DATABASE rai_notebooks TO ROLE rai_developer;\n",
    "-- GRANT USAGE ON SCHEMA rai_notebooks.public TO ROLE rai_developer;\n",
    "-- GRANT CREATE NOTEBOOK ON SCHEMA rai_notebooks.public TO ROLE rai_developer;\n",
    "-- GRANT CREATE SERVICE ON SCHEMA rai_notebooks.public TO ROLE rai_developer;\n",
    "\n",
    "-- Allow Container Runtime notebooks to install packages from PyPI.\n",
    "-- CREATE OR REPLACE NETWORK RULE pypi_network_rule\n",
    "--   MODE = EGRESS\n",
    "--   TYPE = HOST_PORT\n",
    "--   VALUE_LIST = ('pypi.org', 'pypi.python.org', 'pythonhosted.org', 'files.pythonhosted.org');\n",
    "-- CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION pypi_access_integration\n",
    "--   ALLOWED_NETWORK_RULES = (pypi_network_rule)\n",
    "--   ENABLED = true;\n",
    "-- GRANT USAGE ON INTEGRATION pypi_access_integration TO ROLE rai_developer;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83853763",
   "metadata": {},
   "source": [
    "## Step 5 - Set Up Direct Access\n",
    "\n",
    "By default, Python API calls are routed through Snowflake [Service Functions](https://docs.snowflake.com/en/developer-guide/snowpark-container-services/working-with-services#service-functions-using-a-service-from-an-sql-query), which adds some extra overhead to every request.\n",
    "[Direct Access](https://docs.relational.ai/manage/app#direct-access) allows connections straight to the RAI Native App’s secure web address, skipping the middleman and reducing latency—often by about 500ms.\n",
    "\n",
    "> :warning: Note: Snowflake notebooks can't use Direct Access because of network restrictions in those environments.\n",
    "> This applies to both Notebooks in Workspaces and Legacy Notebooks.\n",
    "> If you only plan to use RelationalAI from a Snowflake notebook, and not from a local Python environment, you can skip this step.\n",
    "\n",
    "Currently, Direct Access only supports three authentication methods: OAuth, Personal Access Token (PAT), and key-pair.\n",
    "We recommend creating an [OAuth security integration](https://docs.relational.ai/manage/app#da-auth-create-oauth-security-integration) for Direct Access now so that users can authenticate interactively in their local development environments.\n",
    "PAT or key-pair authentication can be set up later for applications and automation.\n",
    "\n",
    "To create an OAuth security integration, execute the following SQL:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "573b7b88",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "-- Create a public OAuth client for Direct Access.\n",
    "CREATE SECURITY INTEGRATION RAI_SECURITY_INTEGRATION\n",
    "  TYPE = OAUTH\n",
    "  ENABLED = TRUE\n",
    "  OAUTH_CLIENT = CUSTOM\n",
    "  OAUTH_CLIENT_TYPE = 'PUBLIC'  -- or 'CONFIDENTIAL' if needed\n",
    "  OAUTH_ALLOW_NON_TLS_REDIRECT_URI = TRUE\n",
    "  OAUTH_REDIRECT_URI = 'http://localhost:8001/snowflake/oauth-redirect'\n",
    "  OAUTH_ISSUE_REFRESH_TOKENS = TRUE\n",
    "  OAUTH_REFRESH_TOKEN_VALIDITY = 86400;  -- Time in seconds"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d08d21ac",
   "metadata": {},
   "source": [
    "Note that the redirect URI can be any localhost URI, but the port must be open on the user's machine.\n",
    "Now, run the following SQL to retrieve the client ID:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0421bd83",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "-- Inspect the created integration. Change the integration name if you used\n",
    "-- a different one in step 1.\n",
    "DESC SECURITY INTEGRATION RAI_SECURITY_INTEGRATION;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e0fec93",
   "metadata": {},
   "source": [
    "Share the client ID, client secret (if applicable), and redirect URI with users so they can [configure](https://docs.relational.ai/build/guides/configuration#direct-access) their Direct Access connection locally.\n",
    "\n",
    "## Step 6 - Enable Warm Reasoners (Optional)\n",
    "\n",
    "[Warm reasoners](https://docs.relational.ai/manage/compute-resources/#warm-reasoners) are pre-provisioned RAI reasoners that can reduce latency for workloads that require quick startup times.\n",
    "\n",
    "We recommend enabling a warm `logic` reasoner for at least one reasoner size to get the best performance when using the RAI Native App.\n",
    "To enable a warm `logic` reasoner of size `HIGHMEM_X64_S`, run the following SQL command:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3af23dc4",
   "metadata": {
    "language": "sql"
   },
   "outputs": [],
   "source": [
    "-- Note: Warm reasoners incur additional costs. If you want to enable warm reasoners,\n",
    "-- uncomment the line below and run it.\n",
    "-- CALL RELATIONALAI.APP.ENABLE_WARM_REASONER('logic', 'HIGHMEM_X64_S');"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb539a95",
   "metadata": {},
   "source": [
    "See the [Warm Reasoners](https://docs.relational.ai/manage/compute-resources/#warm-reasoners) documentation for more information on warm reasoners and how to manage them.\n",
    "\n",
    "## Congratulations! Your RelationalAI app is now ready to use.\n",
    "\n",
    "# Next Steps\n",
    "\n",
    "To get up and running with RelationalAI, download the [Simple Start template](https://docs.relational.ai/build/templates/simple-start), extract the files,and follow the instructions in the `README` file and the `simple-start.ipynb` notebook.\n",
    "\n",
    "For a more detailed example and more information about the RelationalAI Python library, check out the [documentation](https://docs.relational.ai/build/).\n",
    "\n",
    "Links:\n",
    "\n",
    "- Simple Start Notebook: https://relational.ai/build/templates/simple-start\n",
    "- All templates: https://docs.relational.ai/build/templates\n",
    "- Docs: https://docs.relational.ai\n",
    "\n",
    "## APPENDIX 1 - Deactivate the RAI Native App\n",
    "\n",
    "To reduce costs when you are not using the RAI Native App, suspend CDC, and delete all reasoners.\n",
    "\n",
    "> :warning: Note that this task requires the `app_admin` application role.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af10691a",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "session = get_active_session()\n",
    "# note: the use of `skip_appendix` in the Appendices makes the notebook's\n",
    "# \"Run All\" action skip these cells\n",
    "skip_appendix = True\n",
    "\n",
    "# Deactivate the app\n",
    "if not skip_appendix:\n",
    "    session.sql(\"CALL RELATIONALAI.APP.DEACTIVATE();\").collect()\n",
    "\n",
    "# To re-activate the app:\n",
    "# session.sql(\"CALL RELATIONALAI.APP.ACTIVATE();\").collect()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05fa983b",
   "metadata": {},
   "source": [
    "## APPENDIX 2 - App Upgrades\n",
    "\n",
    "Your RAI Native App is [automatically upgraded](https://docs.relational.ai/manage/app/upgrade) every Monday at 10:00 UTC. During upgrades, the application cannot process RAI transactions.\n",
    "\n",
    "If you prefer to schedule upgrades for a different day and time, use the [`schedule_upgrade()`](https://docs.relational.ai/api/sql/app/schedule_upgrade) procedure:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9768ebc6",
   "metadata": {
    "language": "python"
   },
   "outputs": [],
   "source": [
    "if not skip_appendix:\n",
    "    # Schedule upgrades for Wednesdays at 15:00 UTC. Times are in 24-hour format.\n",
    "    session.sql(\"CALL relationalai.app.schedule_upgrade('WEDNESDAY', '15:00');\").collect()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Streamlit Notebook",
   "name": "streamlit"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
