Engineering

Jev, Built Natively Into Sail

Five Jev functions ship inside Sail’s Rust engine, with nothing to install, so a Spark SQL or DataFrame query can ask Jev about every row of a table, across a cluster.

4 min read Sep 2026

We’ve built five Jev functions into Sail’s SQL, with nothing extra to install. A Spark SQL or DataFrame query can now ask TypeSafe’s Jev about every row of a table, across a cluster, and use each typed answer like any other column. The functions run asynchronously in Sail’s Rust engine, so calls overlap instead of waiting on each other.

Jev Functions in Sail

One function covers each of Jev’s three question types, one asks several questions in a single request, and one lists the models available to your account.

FunctionReturns
jev_noul(state, instructions)The probability that the answer is yes
jev_choice(state, instructions, criteria)The chosen option, a probability for each option, and a confidence
jev_score(state, instructions, criteria)A position on your scale, with probabilities and a confidence
jev_system_one(state, questions)Answers to several questions about the same state, from one request
jev_models()The available models

The state can be a string or a Variant value, and criteria describes the possible answers. Each result is a struct that also records the model that answered and the tokens used, and an optional last argument sets the model, timeout, and retry limits for a call. The functions are native to Sail and are not part of Apache Spark.

The query below classifies the last day’s failed jobs.

SELECT
  job_id,
  jev_choice(
    message,
    'What most likely caused this job to fail?',
    map('bad_input', 'Missing, malformed, or unexpected input data',
        'schema_change', 'A column or type changed upstream',
        'resources', 'Out of memory, disk, or time',
        'code', 'A bug in the job itself')
  ) AS cause
FROM job_errors
WHERE failed_at >= current_date() - INTERVAL 1 DAY;

Each error message goes to Jev with the question and four possible causes. cause.choice holds the most likely cause, cause.probabilities holds a probability for each one, and cause.confidence says how sure the model is. A pipeline can act on confident answers and send the rest to a person for review.

For a missing S3 path and an unresolved column, the label, probabilities, and confidence come back like this.

-RECORD 0-----------------------------------------------------------------------------------
 job_id        | 1
 choice        | bad_input
 probabilities | {bad_input -> 0.97, schema_change -> 0.0, resources -> 0.0, code -> 0.03}
 confidence    | 0.95
-RECORD 1-----------------------------------------------------------------------------------
 job_id        | 2
 choice        | schema_change
 probabilities | {resources -> 0.0, bad_input -> 0.01, code -> 0.13, schema_change -> 0.86}
 confidence    | 0.82

Model Calls Inside the Engine

We built the Jev functions into Sail’s Rust engine, as asynchronous functions within Sail. All Jev calls on a worker share one limit on requests in flight, eight by default, so partitions and concurrent queries cannot multiply the load on the service. Adding workers raises the total, up to the rate limit of your TypeSafe account.

Sail retries rate-limit responses, server errors, and timeouts with backoff, and it follows the service’s Retry-After header. It validates every response, and a malformed one fails the query instead of producing a placeholder answer.

Entity Resolution in One Query

Each answer is an ordinary column, so Jev composes with everything else in a query. Entity resolution is a common case. A CRM and a billing system may record the same customer as Acme Corp and ACME Corporation, and exact matching misses the pair. A join on a cheap key such as the postal code produces candidate pairs, and Jev decides which of them name the same company.

SELECT c.id AS crm_id, b.id AS billing_id
FROM crm_accounts c
JOIN billing_customers b ON c.postal_code = b.postal_code
WHERE jev_noul(
        to_json(named_struct('crm_name', c.name, 'billing_name', b.name)),
        'Do these two names refer to the same company?'
      ).noul >= 0.8;

It returns the one real match.

+------+----------+
|crm_id|billing_id|
+------+----------+
|     1|       101|
+------+----------+

Jev scored Acme Corp and ACME Corporation at 0.93, and Acme Corp and Initech, which share a postal code, at 0.24.

The join does the cheap work across the cluster, and only the candidate pairs it produces reach the model. Results are not cached between queries, so write them to a table when you plan to reuse them.

Getting Started with Sail

The Jev functions ship in Sail 0.7.2. Install or upgrade with pip install pysail==0.7.2, or see the installation guide for building from source and deploying to Kubernetes. Jev is in early access, and you need an API key from TypeSafe.

Getting started takes one environment variable. Set your key for the Sail server before startup, and on a cluster, for every worker too.

export TYPESAFE_API_KEY='<your-api-key>'

Then ask Jev a first question.

SELECT jev_noul(
  'ExecutorLostFailure: container killed for exceeding memory limits',
  'Would rerunning this job unchanged likely fix it?'
).noul AS retry_would_help;
+----------------+
|retry_would_help|
+----------------+
|            0.16|
+----------------+

Jev answers with a probability. Here it is low, because a job that ran out of memory usually fails again when rerun unchanged. The same query runs on a laptop and on a cluster. The Jev guide covers every function, argument, and setting in detail.

Join the Community

Sail wouldn’t have its current shape without its community. We welcome contributions of all kinds, from code and feature ideas to bug reports with reproducible examples. Stay tuned for more feature announcements, and if you put the Jev functions to work, we’d love to hear about it. You can follow along on GitHub or by joining our Slack Community.

Want to run Sail without operating the infrastructure? The LakeSail Platform offers fully managed Sail with built-in governance, observability, and BYOC deployment. Get started with a 14-day free trial.

Questions about your Spark setup?

Book 30 minutes with a LakeSail engineer. Real answers, no pitch.