Skip to main content

Goals

  • Understand when and why to host multiple environments in a single project
  • Define multiple Environment subclasses and serve them from one server
  • Select a specific environment variant from the client

Prerequisites

Introduction

So far, each project we’ve built has had a single Environment class served by a single server. But sometimes you have a family of related environments that share logic, data, or infrastructure. For example, an arithmetic benchmark might have a basic variant (addition and subtraction) and a bitwise variant (AND, OR, XOR). These environments share common patterns - task loading, answer verification, data formats - so it makes sense to keep them in the same codebase rather than maintaining separate projects. An ORS server can host multiple Environment classes. When it does, the relationship between server and environment is no longer one-to-one. Clients need to specify which variant they want to interact with.

Defining multiple environments

Let’s build two arithmetic environments that share a common AnswerParams model but define different tasks and verification logic.
Both environments share AnswerParams and follow the same structure. The only differences are the tasks and the domain.

Serving multiple environments

To serve both environments from a single server, pass them as a list to Server:
Each environment class is registered by its lowercased class name:
  • BasicArithmetic"basicarithmetic"
  • BitwiseArithmetic"bitwisearithmetic"
The first environment in the list is the default. If a client doesn’t specify a variant, it will interact with BasicArithmetic. Run the server:

Selecting a variant from the client

When a server hosts a single environment, you don’t need to specify a variant:
When a server hosts multiple environments, you need to pass the variant parameter to target a specific one:
From here, each environment works exactly as before. You can list tasks, start sessions, and call tools independently:

Organizing your code

The environment classes can live in the same file or in separate modules. For a small number of variants, a single file is fine. As the number of variants grows, splitting them into separate files keeps things manageable:
Shared logic - task spec models, grading utilities, data loading - can go in common modules that each environment imports. This is the main benefit of keeping related environments in one project: you write the shared code once.