Automating React Native Evals With Airflow

Authors
Artur Morys-Magiera
Software Engineer
@
Callstack

React Native Evals is our benchmark suite for testing how coding models handle React Native tasks. A solver is the model being benchmarked: it attempts the task. A judge is the model that checks the generated files against the task's requirements. We keep the files, scores, logs, and run settings so we can trace and compare the results.

We started by running the benchmark by hand. We connected models through OpenCode, adjusted configuration and credentials, started each run, watched for failures, and collected the output ourselves. That worked with a small model list. Once it grew, each release added more runs to manage and more chances to confuse a model regression with a provider problem or an operator mistake.

We now coordinate the process with Apache Airflow, a platform for defining and running workflows made of ordered tasks. A human operator selects the models, eval scope, and repeat count once. Airflow validates that request, starts one worker for each solver/judge pair, and reports each pair's progress and failures separately.

MLflow Tracking keeps the run settings, metrics, and output files connected to the result that produced them. The React Native Evals runner acts as the evaluation harness: it invokes the solver and judge through OpenCode, an AI coding agent. People still check the analyzer output before anything is published.

The workflow follows six steps:

  1. An operator selects the models, evals, and repeat count.
  2. Airflow schedules and monitors the selected solver/judge pairs.
  3. The harness runs the solver, then the judge checks the output.
  4. MLflow keeps the run configuration and artifacts together when tracking is enabled.
  5. Reviewers verify the summaries and analyzer output.
  6. Verified artifacts feed the public benchmark results.
An operator selects models and evals. Airflow schedules each solver and judge pair, the harness runs the solve-and-judge cycle, MLflow keeps the run record and artifacts together, and humans review the result before publication.

Stage 1: choose the run

Operators usually start the workflow from the Airflow user interface. They can also send the same request through the Airflow API.

The operator chooses one judge, one or more solvers, the evals to run, and the repeat count. Operators can enter supported model IDs when a run starts, so testing a new supported model does not require an automation code change or an edit to a YAML model matrix. Operators can run the full benchmark repeatedly or choose a smaller task set for a smoke test.

The request records the exact Evals commit, meaning the specific version of the Evals repository used for that benchmark, along with the solver and judge models, selected tasks, repeat count, and runtime settings. Keeping those inputs with the outputs lets us compare like-for-like runs and reproduce the setup later. The Evals commit pins the benchmark code and task definitions to one concrete repository version; model outputs can still vary between repeats.

Stage 2: solve and judge each task

Airflow starts a separate containerized worker for each selected solver/judge pair. Each worker keeps its workspace, logs, and failures separate. If a provider times out or a model returns malformed output, the operator can inspect that pair instead of searching through one combined log.

The React Native Evals runner is the evaluation harness. It invokes both models through OpenCode. During the solver stage, the harness hides unselected evals, reference solutions, and judge-only requirements. It restores the requirements before judging, while the reference solution remains unavailable.

Each pair completes its configured repeats in sequence. Airflow records each worker's state and marks the workflow successful only when all required pairs finish.

Stage 3: keep the run record and files together

Workers are short-lived, so we need a durable record of their outputs after execution ends. We use MLflow Tracking to keep each model pair's configuration, status, metrics, generated files, scores, and summaries together for comparison and review.

Each solver/judge pair has its own MLflow experiment, which keeps all runs for that pair together. Every Airflow workflow execution creates one tracking run for each selected pair, and that record contains the configured repeats. Airflow checks that each expected repeat produced its output before it marks the workflow successful.

MLflow is enabled by default, but the runner can operate without it. If tracking is disabled, the runner can use a separate export for the normalized run files.

Stage 4: review and publish the result

After the configured repeats succeed with MLflow enabled, the runner builds a dated archive in the format expected by the React Native Evals results analyzer. Reviewers can download it, combine it with other result sources when needed, and process it without rebuilding the folder structure by hand.

We inspect the generated summaries and analyzer output before committing any result files used by the public benchmark. Human review remains the publication gate.

What changed for the operator

One request now replaces repeated setup and monitoring for each solver/judge pair. Automation handles execution, judging, progress reporting, logs, and artifact packaging. People focus on checking whether the result is valid.

Adding a supported model now requires its model ID, a judge, and an eval scope rather than a code change or model-matrix edit. In current team practice, adding a supported model and completing its evals can take about a day instead of taking about a week with the manual setup. This comparison only covers completed evals. It does not include review or publication.

Because the benchmark runs as an Airflow workflow, we can also schedule it or trigger it from a specific external event. Human review remains the final gate before results reach the public benchmark.

The latest reviewed React Native Evals results are available at rn-evals.vercel.app.

Insights

Learn more about CI/CD

cover

Jun 19Article

Caching React Native Builds on S3 and R2 With Rock

Learn how to cache your React Native builds and store them on Amazon S3 or R2 to reuse across your team.

cover

Oct 29Podcast

How to Handle App Center Retirement

Discover App Center alternatives for React Native. Explore migration tips and Expo's role in the transition from CodePush & other App Center functionalities.

cover

Aug 4Article

Mastering Fastlane: Overcoming Challenges in Passing Build Properties to Native Layers

Master passing build properties with Fastlane for efficient application development.

cover

May 14Article

Ship OTA (Over-The-Air) When in an Emergency

Shipping Over-The-Air may save your app from a lot of trouble. Find out why and how you can make your application OTA-ready.

cover

May 10Article

Shipping Fast With Continuous Deployment

Continuous Deployment (CD) lets you ship new features or check for critical bugs fast. Find out more about developing apps with a CD setup.

cover

Dec 13Article

How Can Continuous Integration (CI) Improve Your React Native Apps?

Read the article and find out how Continuous Integration (CI) can improve the building, testing, and distribution of your React Native apps.

cover

Apr 24Article

Continuous Delivery - Integration for Everyone

A quick guide on how to tame Continuous Delivery and Integration. Dive into a real-world scenario from a Callstack client project.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.