> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synthefy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Snowflake

> Run Nori on your Snowflake data directly from SQL — no data export, no training.

Call Nori from Snowflake SQL to predict numeric targets on your tables. Nori is
**in-context**: you pass some labeled rows as examples and the rows you want
scored, and it returns one prediction per row in a single forward pass — no
training or fine-tuning.

<CardGroup cols={2}>
  <Card title="Hosted connector" icon="bolt" href="#hosted-connector">
    A SQL function that calls the hosted Nori endpoint. Fastest to set up — works today.
  </Card>

  <Card title="Native App (in-account)" icon="shield-halved" href="#native-app-in-account">
    Runs Nori inside your Snowflake account via Snowpark Container Services. Your data never leaves the account. Coming to Snowflake Marketplace.
  </Card>
</CardGroup>

***

## Hosted connector

A Snowflake external-access function calls the hosted Nori endpoint. The model
runs on Synthefy's cloud; Snowflake sends the rows and gets predictions back.

<Note>
  The rows you pass are sent to the hosted endpoint for scoring. If your workload
  requires data to stay inside your Snowflake account, use the [Native App](#native-app-in-account) instead.
</Note>

**Prerequisites**

<Steps>
  <Step title="ACCOUNTADMIN role">
    Needed to create the external access integration (`CREATE INTEGRATION`).
  </Step>

  <Step title="A Nori API key">
    See [API key](/setup/api_key).
  </Step>

  <Step title="A warehouse">
    Any warehouse, to run the function.
  </Step>
</Steps>

### Set up (once)

Run each step in a Snowsight worksheet as `ACCOUNTADMIN`.

<Steps>
  <Step title="Create a namespace">
    ```sql Namespace theme={null}
    CREATE DATABASE IF NOT EXISTS NORI;
    CREATE SCHEMA   IF NOT EXISTS NORI.PUBLIC;
    ```
  </Step>

  <Step title="Add the egress rule and your API key">
    Replace `<YOUR_NORI_API_KEY>` with your key (highlighted line).

    ```sql Network rule + secret {6} theme={null}
    CREATE OR REPLACE NETWORK RULE NORI.PUBLIC.nori_api_network_rule
      MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('inference.baseten.co:443');

    CREATE OR REPLACE SECRET NORI.PUBLIC.nori_api_key
      TYPE = GENERIC_STRING
      SECRET_STRING = '<YOUR_NORI_API_KEY>';
    ```
  </Step>

  <Step title="Create the external access integration">
    ```sql Integration theme={null}
    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION nori_api_integration
      ALLOWED_NETWORK_RULES          = (NORI.PUBLIC.nori_api_network_rule)
      ALLOWED_AUTHENTICATION_SECRETS = (NORI.PUBLIC.nori_api_key)
      ENABLED = TRUE;
    ```
  </Step>

  <Step title="Create the prediction function">
    Uses only the Python standard library — nothing to install.

    ```sql nori_predict theme={null}
    CREATE OR REPLACE FUNCTION NORI.PUBLIC.nori_predict(x_train ARRAY, y_train ARRAY, x_test ARRAY)
    RETURNS ARRAY
    LANGUAGE PYTHON
    RUNTIME_VERSION = '3.11'
    HANDLER = 'predict'
    EXTERNAL_ACCESS_INTEGRATIONS = (nori_api_integration)
    SECRETS = ('api_key' = NORI.PUBLIC.nori_api_key)
    AS
    $$
    import json, _snowflake, urllib.request

    def predict(x_train, y_train, x_test):
        api_key = _snowflake.get_generic_secret_string("api_key")
        body = json.dumps({
            "model": "synthefy/nori", "task": "regression",
            "X_train": x_train, "y_train": y_train, "X_test": x_test,
        }).encode()
        req = urllib.request.Request(
            "https://inference.baseten.co/predict", data=body,
            headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
            method="POST")
        with urllib.request.urlopen(req, timeout=300) as r:
            return json.loads(r.read().decode()).get("predictions")
    $$;
    ```
  </Step>
</Steps>

### Predict

<Tabs>
  <Tab title="Smoke test">
    Returns two numbers — confirms the connector works end to end.

    ```sql Smoke test theme={null}
    SELECT NORI.PUBLIC.nori_predict(
      [[0.0,1.0],[1.0,0.0],[0.5,0.5],[0.2,0.8]]::ARRAY,  -- X_train
      [0.1,0.9,0.5,0.3]::ARRAY,                           -- y_train
      [[0.3,0.7],[0.8,0.2]]::ARRAY                        -- X_test
    );
    -- [ 0.36, 0.74 ]
    ```
  </Tab>

  <Tab title="Predict from a table">
    Rows with a `NULL` target are scored from the rows where it's set. Aggregate
    `x_train` and `y_train` with the **same** `ORDER BY` so features and targets stay aligned.

    ```sql Table prediction theme={null}
    WITH ctx AS (   -- labeled rows
      SELECT ARRAY_AGG(ARRAY_CONSTRUCT(feat1, feat2, feat3)) WITHIN GROUP (ORDER BY id) AS x_train,
             ARRAY_AGG(target)                               WITHIN GROUP (ORDER BY id) AS y_train
      FROM my_table WHERE target IS NOT NULL
    ),
    qry AS (        -- rows to score
      SELECT ARRAY_AGG(ARRAY_CONSTRUCT(feat1, feat2, feat3)) WITHIN GROUP (ORDER BY id) AS x_test,
             ARRAY_AGG(id)                                   WITHIN GROUP (ORDER BY id) AS ids
      FROM my_table WHERE target IS NULL
    ),
    res AS (
      SELECT NORI.PUBLIC.nori_predict(ctx.x_train, ctx.y_train, qry.x_test) AS preds, qry.ids AS ids
      FROM ctx, qry
    )
    SELECT res.ids[f.index]::NUMBER AS id, f.value::FLOAT AS prediction
    FROM res, LATERAL FLATTEN(input => res.preds) f
    ORDER BY id;
    ```
  </Tab>
</Tabs>

<Accordion title="Full setup script (copy-paste)">
  ```sql setup.sql theme={null}
  CREATE DATABASE IF NOT EXISTS NORI;
  CREATE SCHEMA   IF NOT EXISTS NORI.PUBLIC;

  CREATE OR REPLACE NETWORK RULE NORI.PUBLIC.nori_api_network_rule
    MODE = EGRESS TYPE = HOST_PORT VALUE_LIST = ('inference.baseten.co:443');

  CREATE OR REPLACE SECRET NORI.PUBLIC.nori_api_key
    TYPE = GENERIC_STRING SECRET_STRING = '<YOUR_NORI_API_KEY>';

  CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION nori_api_integration
    ALLOWED_NETWORK_RULES          = (NORI.PUBLIC.nori_api_network_rule)
    ALLOWED_AUTHENTICATION_SECRETS = (NORI.PUBLIC.nori_api_key)
    ENABLED = TRUE;

  CREATE OR REPLACE FUNCTION NORI.PUBLIC.nori_predict(x_train ARRAY, y_train ARRAY, x_test ARRAY)
  RETURNS ARRAY
  LANGUAGE PYTHON
  RUNTIME_VERSION = '3.11'
  HANDLER = 'predict'
  EXTERNAL_ACCESS_INTEGRATIONS = (nori_api_integration)
  SECRETS = ('api_key' = NORI.PUBLIC.nori_api_key)
  AS
  $$
  import json, _snowflake, urllib.request

  def predict(x_train, y_train, x_test):
      api_key = _snowflake.get_generic_secret_string("api_key")
      body = json.dumps({
          "model": "synthefy/nori", "task": "regression",
          "X_train": x_train, "y_train": y_train, "X_test": x_test,
      }).encode()
      req = urllib.request.Request(
          "https://inference.baseten.co/predict", data=body,
          headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
          method="POST")
      with urllib.request.urlopen(req, timeout=300) as r:
          return json.loads(r.read().decode()).get("predictions")
  $$;
  ```
</Accordion>

### Notes

<Note>
  **Numeric features only.** Encode categorical columns (one-hot or ordinal) before calling.
</Note>

<Warning>
  **Cold start.** The first call after idle can take up to \~2 minutes while the model warms up; subsequent calls are fast.
</Warning>

<Note>
  **Size & alignment.** Snowflake `ARRAY` values cap at 16 MB — sample the labeled rows for very large context tables. Always aggregate `x_train` and `y_train` with the same `ORDER BY`.
</Note>

***

## Native App (in-account)

For workloads that require data to **stay inside your Snowflake account**, Nori is
also available as a **Snowflake Native App** that runs the model in-account using
**Snowpark Container Services** — no data leaves your account. You call it exactly
like the hosted connector:

```sql In-account call theme={null}
SELECT nori_app.svc.nori_predict_svc(x_train, y_train, x_test);
```

<Note>
  The in-account Native App is rolling out via the Snowflake Marketplace. Contact
  [support@synthefy.com](mailto:support@synthefy.com) for early access.
</Note>
