Buckets:

download
raw
61.7 kB
<meta charset="utf-8" /><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;The Task API&quot;,&quot;local&quot;:&quot;the-task-api&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;The Short Answer&quot;,&quot;local&quot;:&quot;the-short-answer&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;When You Need It&quot;,&quot;local&quot;:&quot;when-you-need-it&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The Protocol&quot;,&quot;local&quot;:&quot;the-protocol&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Two rules that are easy to miss&quot;,&quot;local&quot;:&quot;two-rules-that-are-easy-to-miss&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;HTTP Routes&quot;,&quot;local&quot;:&quot;http-routes&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;env_name&quot;,&quot;local&quot;:&quot;envname&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Split normalization&quot;,&quot;local&quot;:&quot;split-normalization&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Error semantics&quot;,&quot;local&quot;:&quot;error-semantics&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Implementing It&quot;,&quot;local&quot;:&quot;implementing-it&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Selecting a task in reset()&quot;,&quot;local&quot;:&quot;selecting-a-task-in-reset&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Streaming and very large splits&quot;,&quot;local&quot;:&quot;streaming-and-very-large-splits&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Consuming the Task API&quot;,&quot;local&quot;:&quot;consuming-the-task-api&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Imported Environments&quot;,&quot;local&quot;:&quot;imported-environments&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Checklist&quot;,&quot;local&quot;:&quot;checklist&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Related Reading&quot;,&quot;local&quot;:&quot;related-reading&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/>
<link href="/docs/openenv/main/en/_app/immutable/entry/start.B8Noo2MH.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/grupJYVP.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/CkB8YxkR.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/entry/app.CKFy4Q6n.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/DSBQ3IWM.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/Cs50_Xpk.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/DsnmJJEf.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/BJRjUCyW.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/nodes/0.DCHfj6lY.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/nodes/23.DmcNc5uU.js" rel="modulepreload">
<link href="/docs/openenv/main/en/_app/immutable/chunks/BuGzQgii.js" rel="modulepreload">
<!--8dv0h9--><meta name="hf:doc:metadata" content="{&quot;title&quot;:&quot;The Task API&quot;,&quot;local&quot;:&quot;the-task-api&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;The Short Answer&quot;,&quot;local&quot;:&quot;the-short-answer&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;When You Need It&quot;,&quot;local&quot;:&quot;when-you-need-it&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;The Protocol&quot;,&quot;local&quot;:&quot;the-protocol&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Two rules that are easy to miss&quot;,&quot;local&quot;:&quot;two-rules-that-are-easy-to-miss&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;HTTP Routes&quot;,&quot;local&quot;:&quot;http-routes&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;env_name&quot;,&quot;local&quot;:&quot;envname&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Split normalization&quot;,&quot;local&quot;:&quot;split-normalization&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Error semantics&quot;,&quot;local&quot;:&quot;error-semantics&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Implementing It&quot;,&quot;local&quot;:&quot;implementing-it&quot;,&quot;sections&quot;:[{&quot;title&quot;:&quot;Selecting a task in reset()&quot;,&quot;local&quot;:&quot;selecting-a-task-in-reset&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3},{&quot;title&quot;:&quot;Streaming and very large splits&quot;,&quot;local&quot;:&quot;streaming-and-very-large-splits&quot;,&quot;sections&quot;:[],&quot;depth&quot;:3}],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Consuming the Task API&quot;,&quot;local&quot;:&quot;consuming-the-task-api&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Imported Environments&quot;,&quot;local&quot;:&quot;imported-environments&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Checklist&quot;,&quot;local&quot;:&quot;checklist&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2},{&quot;title&quot;:&quot;Related Reading&quot;,&quot;local&quot;:&quot;related-reading&quot;,&quot;sections&quot;:[],&quot;depth&quot;:2}],&quot;depth&quot;:1}"/><!---->
<link href="/docs/openenv/main/en/_app/immutable/assets/0.tn0RQdqM.css" rel="modulepreload"> <!--[--><!--[0--><!--[--><!--[0--><!--[--><p></p> <div class="items-center shrink-0 min-w-[100px] max-sm:min-w-[50px] justify-end ml-auto flex" style="float: right; margin-left: 10px; display: inline-flex; position: relative; z-index: 10;"><div class="inline-flex rounded-md max-sm:rounded-sm"><button class="inline-flex items-center gap-1 h-7 max-sm:h-7 px-2 max-sm:px-1.5 text-sm font-medium text-gray-800 border border-r-0 rounded-l-md max-sm:rounded-l-sm border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-live="polite"><span class="inline-flex items-center justify-center rounded-md p-0.5 max-sm:p-0 hover:text-gray-800 dark:hover:text-gray-200"><svg class="sm:size-3.5 size-3" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----></span> <span>Copy page</span></button> <button class="inline-flex items-center justify-center w-6 max-sm:w-5 h-7 max-sm:h-7 disabled:pointer-events-none text-sm text-gray-500 hover:text-gray-700 dark:hover:text-white rounded-r-md max-sm:rounded-r-sm border border-l transition border-gray-200 bg-white hover:shadow-inner dark:border-gray-850 dark:bg-gray-950 dark:text-gray-200 dark:hover:bg-gray-800" aria-haspopup="menu" aria-expanded="false" aria-label="Open copy menu"><svg class="transition-transform text-gray-400 overflow-visible sm:size-3.5 size-3 rotate-0" width="1em" height="1em" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1L6 6L11 1" stroke="currentColor"></path></svg><!----></button></div> <!--[-1--><!--]--></div><!----> <!--[0--><h1 class="relative group"><a id="the-task-api" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-task-api"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>The Task API</span></h1><!--]--><!----> <p>This guide explains the Task API: the optional discovery layer that lets a dataset-backed environment publish the <em>set of tasks</em> it can run, so a trainer or evaluator can enumerate them and choose which one each episode uses.</p> <p>It exists to answer a common question: OpenEnv’s step loop gives you one episode at a time, so how does a training script find out that an environment holds 7,595 test problems, and how does it ask for problem number 12?</p> <!--[1--><h2 class="relative group"><a id="the-short-answer" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-short-answer"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>The Short Answer</span></h2><!--]--><!----> <p>The Task API has two halves:</p> <ul><li><strong>Server side</strong>: your environment optionally implements five methods — <code>list_splits()</code>, <code>list_tasks()</code>, <code>num_tasks()</code>, <code>get_task()</code>, <code>get_task_range()</code>. These are described by the <code>TaskProvider</code> protocol.</li> <li><strong>HTTP side</strong>: when those methods exist, the environment server automatically exposes them as routes under <code>/{env_name}/…</code>. You do not register anything.</li></ul> <p>Task discovery is <strong>metadata only</strong>. It never starts an episode. Selecting a task happens the usual way, through <code>reset()</code>:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!---->env.list_splits() <span class="hljs-comment"># [&quot;train&quot;, &quot;test&quot;]</span>
env.num_tasks(<span class="hljs-string">&quot;test&quot;</span>) <span class="hljs-comment"># 7595</span>
env.get_task(<span class="hljs-string">&quot;test&quot;</span>, <span class="hljs-number">12</span>) <span class="hljs-comment"># {&quot;id&quot;: &quot;test-12&quot;, &quot;index&quot;: 12, &quot;split&quot;: &quot;test&quot;}</span>
env.reset(split=<span class="hljs-string">&quot;test&quot;</span>, index=<span class="hljs-number">12</span>) <span class="hljs-comment"># &lt;- this is what starts the episode</span><!----></pre></div><!----> <p>The API is shaped to be compatible with ORS/OpenReward task and split conventions, which is why environments imported with <code>openenv import</code> get it for free.</p> <!--[1--><h2 class="relative group"><a id="when-you-need-it" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#when-you-need-it"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>When You Need It</span></h2><!--]--><!----> <p>Implement the Task API when your environment is <strong>dataset-backed</strong> — when an episode means “run one row of a dataset” and a trainer needs to iterate, shard, or shuffle over those rows.</p> <p>You do <strong>not</strong> need it for environments where every episode is generated or self-contained: Echo, Snake, a game, a REPL. Those environments simply omit the methods, and the routes report <code>501 Not Implemented</code>.</p> <!--[1--><h2 class="relative group"><a id="the-protocol" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#the-protocol"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>The Protocol</span></h2><!--]--><!----> <p><code>TaskProvider</code> is a <code>typing.Protocol</code>, not a base class. You do not inherit from it — you just define the methods on your <code>Environment</code> subclass and the server discovers them by name.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Any</span>, <span class="hljs-type">Optional</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">TaskProvider</span>(<span class="hljs-title class_ inherited__">Protocol</span>):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_splits</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-type">Any</span>]: ...
<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_tasks</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-type">Any</span>]: ...
<span class="hljs-keyword">def</span> <span class="hljs-title function_">num_tasks</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">int</span>: ...
<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_task</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span>, index: <span class="hljs-built_in">int</span></span>) -&gt; <span class="hljs-type">Any</span>: ...
<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_task_range</span>(<span class="hljs-params">
self,
split: <span class="hljs-built_in">str</span>,
start: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>,
stop: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>,
</span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-type">Any</span>]: ...<!----></pre></div><!----> <p>Each method may be sync or <code>async</code>; the server awaits the result when it is awaitable, so you can back task discovery with async dataset clients.</p> <p>A “task spec” is deliberately untyped (<code>Any</code>). Return whatever your environment needs — a dict, a Pydantic model, a dataclass. The server converts it to JSON, handling Pydantic models, dataclasses, and plain objects. In practice a positional stub such as <code>{"id": "test-12", "index": 12, "split": "test"}</code> is enough, because <code>reset()</code> is what actually loads the row.</p> <!--[2--><h3 class="relative group"><a id="two-rules-that-are-easy-to-miss" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#two-rules-that-are-easy-to-miss"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Two rules that are easy to miss</span></h3><!--]--><!----> <ol><li><strong>Task methods must be side-effect-free.</strong> They are discovery, not control. They must not mutate episode state, consume a stream, or advance a cursor.</li> <li><strong>They must work on a freshly constructed environment.</strong> Each task route builds a short-lived environment instance, calls the one method, and closes it again. Nothing you set up in <code>reset()</code> is available, so read configuration in <code>__init__</code> (or lazily inside the method) rather than relying on episode state.</li></ol> <!--[1--><h2 class="relative group"><a id="http-routes" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#http-routes"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>HTTP Routes</span></h2><!--]--><!----> <p>The routes are registered by <code>HTTPEnvServer</code> and appear in the server’s OpenAPI schema under the <code>Task API</code> tag. All of them are namespaced by environment name except <code>/list_environments</code>.</p> <table><thead><tr><th>Method</th><th>Route</th><th>Body</th><th>Response</th></tr></thead><tbody><tr><td><code>GET</code></td><td><code>/list_environments</code></td><td>—</td><td><code>["latex_ocr_env"]</code></td></tr><tr><td><code>GET</code></td><td><code>/{env_name}/splits</code></td><td>—</td><td><code>[{"name": "train", "type": "train"}, …]</code></td></tr><tr><td><code>POST</code></td><td><code>/{env_name}/tasks</code></td><td><code>{"split": "test"}</code></td><td><code>{"tasks": [...], "env_name": "latex_ocr_env"}</code></td></tr><tr><td><code>POST</code></td><td><code>/{env_name}/num_tasks</code></td><td><code>{"split": "test"}</code></td><td><code>{"num_tasks": 7595}</code></td></tr><tr><td><code>POST</code></td><td><code>/{env_name}/task</code></td><td><code>{"split": "test", "index": 12}</code></td><td><code>{"task": {...}}</code></td></tr><tr><td><code>POST</code></td><td><code>/{env_name}/task_range</code></td><td><code>{"split": "test", "start": 0, "stop": 32}</code></td><td><code>{"tasks": [...]}</code></td></tr></tbody></table> <p>The request bodies are <code>ListTasksRequest</code>, <code>NumTasksRequest</code>, <code>GetTaskRequest</code>, and <code>GetTaskRangeRequest</code>. <code>start</code> and <code>stop</code> follow Python slice semantics: <code>start</code> is inclusive, <code>stop</code> is exclusive, and both may be omitted.</p> <p>These are HTTP-only. There are no WebSocket message types for task discovery — the <code>/ws</code> session protocol stays focused on <code>reset</code>, <code>step</code>, <code>state</code>, and <code>close</code>.</p> <!--[2--><h3 class="relative group"><a id="envname" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#envname"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>env_name</span></h3><!--]--><!----> <p><code>{env_name}</code> is the name passed to <code>create_app(..., env_name="latex_ocr_env")</code>. If you do not pass one, it defaults to the environment factory’s class name (for example <code>LatexOCREnvironment</code>). Matching is case-insensitive, and any other name returns <code>404</code>. Pass <code>env_name</code> explicitly for any environment that implements the Task API — the generated URL is part of your public surface, and a class rename should not break a trainer.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">from</span> openenv.core.env_server <span class="hljs-keyword">import</span> create_app
app = create_app(
LatexOCREnvironment,
LatexOCRAction,
LatexOCRObservation,
env_name=<span class="hljs-string">&quot;latex_ocr_env&quot;</span>,
)<!----></pre></div><!----> <!--[2--><h3 class="relative group"><a id="split-normalization" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#split-normalization"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Split normalization</span></h3><!--]--><!----> <p><code>list_splits()</code> may return plain strings, dicts, or Pydantic models. The server normalizes each entry to <code>{"name": ..., "type": ...}</code>:</p> <ul><li>a dict or model is passed through as-is (after JSON conversion)</li> <li>a string becomes <code>{"name": s, "type": s}</code> when <code>s</code> is <code>train</code>, <code>validation</code>, or <code>test</code></li> <li>any other string becomes <code>{"name": s, "type": "validation"}</code></li></ul> <p>So returning <code>["train", "holdout"]</code> yields <code>[{"name": "train", "type": "train"}, {"name": "holdout", "type": "validation"}]</code>. Return dicts yourself if you need to control the <code>type</code> for a custom split name.</p> <!--[2--><h3 class="relative group"><a id="error-semantics" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#error-semantics"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Error semantics</span></h3><!--]--><!----> <table><thead><tr><th>Condition</th><th>Status</th></tr></thead><tbody><tr><td><code>{env_name}</code> does not match the server’s environment</td><td><code>404 Not Found</code></td></tr><tr><td>Environment does not define the method, or it raises <code>NotImplementedError</code></td><td><code>501 Not Implemented</code></td></tr><tr><td>Method raises <code>IndexError</code> (index out of range)</td><td><code>400 Bad Request</code></td></tr></tbody></table> <p>Raise <code>IndexError</code> from <code>get_task()</code> for an out-of-range index so callers get a <code>400</code> rather than a <code>500</code>. Reserve <code>NotImplementedError</code> for capabilities the environment genuinely does not support — for example, random access on a streaming split.</p> <!--[1--><h2 class="relative group"><a id="implementing-it" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#implementing-it"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Implementing It</span></h2><!--]--><!----> <p>A dataset-backed environment reads its dataset in <code>__init__</code>, answers discovery from metadata, and loads the actual row in <code>reset()</code>:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> <span class="hljs-type">Any</span>, <span class="hljs-type">Optional</span>
<span class="hljs-keyword">from</span> uuid <span class="hljs-keyword">import</span> uuid4
<span class="hljs-keyword">from</span> openenv.core.env_server <span class="hljs-keyword">import</span> Environment
<span class="hljs-keyword">from</span> openenv.core.env_server.types <span class="hljs-keyword">import</span> State
<span class="hljs-keyword">class</span> <span class="hljs-title class_">LatexOCREnvironment</span>(<span class="hljs-title class_ inherited__">Environment</span>):
SPLITS = [<span class="hljs-string">&quot;train&quot;</span>, <span class="hljs-string">&quot;test&quot;</span>]
<span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, dataset_name: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;unsloth/LaTeX_OCR&quot;</span></span>) -&gt; <span class="hljs-literal">None</span>:
<span class="hljs-built_in">super</span>().__init__()
<span class="hljs-variable language_">self</span>.dataset_name = dataset_name
<span class="hljs-comment"># --- Task API: discovery only, no side effects ---</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_splits</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-built_in">str</span>]:
<span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>.SPLITS
<span class="hljs-keyword">def</span> <span class="hljs-title function_">num_tasks</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">int</span>:
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(<span class="hljs-variable language_">self</span>._load_split(split))
<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_tasks</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]]:
<span class="hljs-keyword">return</span> [
{<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-string">f&quot;<span class="hljs-subst">{split}</span>-<span class="hljs-subst">{i}</span>&quot;</span>, <span class="hljs-string">&quot;index&quot;</span>: i}
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-variable language_">self</span>.num_tasks(split))
]
<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_task</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span>, index: <span class="hljs-built_in">int</span></span>) -&gt; <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]:
n = <span class="hljs-variable language_">self</span>.num_tasks(split)
<span class="hljs-keyword">if</span> index &lt; <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> index &gt;= n:
<span class="hljs-keyword">raise</span> IndexError(<span class="hljs-string">f&quot;index <span class="hljs-subst">{index}</span> out of range for split <span class="hljs-subst">{split}</span> (n=<span class="hljs-subst">{n}</span>)&quot;</span>)
<span class="hljs-keyword">return</span> {<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-string">f&quot;<span class="hljs-subst">{split}</span>-<span class="hljs-subst">{index}</span>&quot;</span>, <span class="hljs-string">&quot;index&quot;</span>: index, <span class="hljs-string">&quot;split&quot;</span>: split}
<span class="hljs-keyword">def</span> <span class="hljs-title function_">get_task_range</span>(<span class="hljs-params">
self, split: <span class="hljs-built_in">str</span>, start: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>, stop: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>
</span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]]:
n = <span class="hljs-variable language_">self</span>.num_tasks(split)
start = <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> start <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">else</span> start
stop = n <span class="hljs-keyword">if</span> stop <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">else</span> <span class="hljs-built_in">min</span>(stop, n)
<span class="hljs-keyword">return</span> [
{<span class="hljs-string">&quot;id&quot;</span>: <span class="hljs-string">f&quot;<span class="hljs-subst">{split}</span>-<span class="hljs-subst">{i}</span>&quot;</span>, <span class="hljs-string">&quot;index&quot;</span>: i, <span class="hljs-string">&quot;split&quot;</span>: split}
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(start, stop)
]
<span class="hljs-comment"># --- Episode: this is where a task is actually loaded ---</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">reset</span>(<span class="hljs-params">
self,
split: <span class="hljs-built_in">str</span> = <span class="hljs-string">&quot;test&quot;</span>,
index: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>,
seed: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">int</span>] = <span class="hljs-literal">None</span>,
episode_id: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] = <span class="hljs-literal">None</span>,
**kwargs: <span class="hljs-type">Any</span>,
</span>) -&gt; LatexOCRObservation:
<span class="hljs-keyword">if</span> split <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">self</span>.list_splits():
<span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f&quot;unknown split <span class="hljs-subst">{split!r}</span>; expected <span class="hljs-subst">{self.list_splits()}</span>&quot;</span>)
<span class="hljs-variable language_">self</span>._state = State(episode_id=episode_id <span class="hljs-keyword">or</span> <span class="hljs-built_in">str</span>(uuid4()), step_count=<span class="hljs-number">0</span>)
row = <span class="hljs-variable language_">self</span>._load_row(split, index, seed)
<span class="hljs-keyword">return</span> LatexOCRObservation(image_base64=row[<span class="hljs-string">&quot;image&quot;</span>], done=<span class="hljs-literal">False</span>)<!----></pre></div><!----> <!--[2--><h3 class="relative group"><a id="selecting-a-task-in-reset" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#selecting-a-task-in-reset"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Selecting a task in reset()</span></h3><!--]--><!----> <p><code>ResetRequest</code> allows extra fields, and both the HTTP <code>/reset</code> route and the WebSocket <code>reset</code> message forward unknown keys to your <code>reset()</code> — after filtering them against its signature. So naming the parameters <code>split</code> and <code>index</code> in <code>reset()</code> is all it takes for this to work:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!---->result = env.reset(split=<span class="hljs-string">&quot;test&quot;</span>, index=<span class="hljs-number">12</span>)<!----></pre></div><!----> <p>The corresponding wire calls are <code>POST /reset</code> with <code>{"split": "test", "index": 12}</code>, or a WebSocket <code>{"type": "reset", "data": {"split": "test", "index": 12}}</code>. The server filters incoming keys against your <code>reset()</code> signature, so a key it does not declare is dropped silently unless the signature also has <code>**kwargs</code>. A misspelled parameter therefore shows up as “the environment ignored my selection” rather than an error — worth checking first when a task selection appears not to take effect.</p> <p>When <code>index</code> is omitted, pick a row from <code>seed</code> so episodes stay reproducible.</p> <!--[2--><h3 class="relative group"><a id="streaming-and-very-large-splits" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#streaming-and-very-large-splits"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Streaming and very large splits</span></h3><!--]--><!----> <p><code>list_tasks()</code> and <code>get_task_range()</code> can be asked for more rows than you want to materialize. Two defenses are worth building in:</p> <ul><li>return positional stubs rather than real rows, and cap how many you generate (<code>list_tasks()</code> returning a bounded preview is fine — <code>num_tasks()</code> still reports the honest total)</li> <li>if a split is streamed sequentially and cannot be randomly accessed, raise <code>ValueError</code> or <code>NotImplementedError</code> from <code>reset(index=...)</code> and say which mode does support indexing</li></ul> <p>Report the true count from <code>num_tasks()</code> even when <code>list_tasks()</code> is truncated. A trainer that shards work by <code>num_tasks()</code> needs the real denominator.</p> <!--[1--><h2 class="relative group"><a id="consuming-the-task-api" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#consuming-the-task-api"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Consuming the Task API</span></h2><!--]--><!----> <p>The core clients (<code>EnvClient</code>, <code>SyncEnvClient</code>) do not ship Task API methods, because task specs are environment-specific. Environment clients add thin HTTP helpers alongside the inherited Gym-style <code>reset()</code>/<code>step()</code>:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">import</span> requests
ENV_NAME = <span class="hljs-string">&quot;latex_ocr_env&quot;</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">LatexOCREnv</span>(EnvClient[LatexOCRAction, LatexOCRObservation, State]):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">_task_url</span>(<span class="hljs-params">self, path: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">str</span>:
<span class="hljs-comment"># `base_url` may be ws(s)://, and the Task API is plain HTTP.</span>
base = <span class="hljs-variable language_">self</span>.base_url.replace(<span class="hljs-string">&quot;ws://&quot;</span>, <span class="hljs-string">&quot;http://&quot;</span>, <span class="hljs-number">1</span>).replace(<span class="hljs-string">&quot;wss://&quot;</span>, <span class="hljs-string">&quot;https://&quot;</span>, <span class="hljs-number">1</span>)
<span class="hljs-keyword">return</span> <span class="hljs-string">f&quot;<span class="hljs-subst">{base}</span>/<span class="hljs-subst">{ENV_NAME}</span>/<span class="hljs-subst">{path}</span>&quot;</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">list_splits</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-built_in">str</span>]:
resp = requests.get(<span class="hljs-variable language_">self</span>._task_url(<span class="hljs-string">&quot;splits&quot;</span>), timeout=<span class="hljs-number">30</span>)
resp.raise_for_status()
<span class="hljs-keyword">return</span> [s[<span class="hljs-string">&quot;name&quot;</span>] <span class="hljs-keyword">for</span> s <span class="hljs-keyword">in</span> resp.json()]
<span class="hljs-keyword">def</span> <span class="hljs-title function_">num_tasks</span>(<span class="hljs-params">self, split: <span class="hljs-built_in">str</span></span>) -&gt; <span class="hljs-built_in">int</span>:
resp = requests.post(
<span class="hljs-variable language_">self</span>._task_url(<span class="hljs-string">&quot;num_tasks&quot;</span>),
json={<span class="hljs-string">&quot;split&quot;</span>: split},
timeout=<span class="hljs-number">60</span>,
)
resp.raise_for_status()
<span class="hljs-keyword">return</span> <span class="hljs-built_in">int</span>(resp.json()[<span class="hljs-string">&quot;num_tasks&quot;</span>])<!----></pre></div><!----> <p>Note that the Task API is served over HTTP while episodes typically run over the WebSocket <code>/ws</code> session. A client that was constructed from a <code>ws://</code> URL has to derive the HTTP base URL before calling these routes.</p> <p>From a training or evaluation loop, discovery and episode control then compose naturally:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-python "><!----><span class="hljs-keyword">with</span> LatexOCREnv.from_docker_image(<span class="hljs-string">&quot;latex-ocr-env:latest&quot;</span>).sync() <span class="hljs-keyword">as</span> env:
total = env.num_tasks(<span class="hljs-string">&quot;test&quot;</span>)
<span class="hljs-keyword">for</span> index <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(total):
result = env.reset(split=<span class="hljs-string">&quot;test&quot;</span>, index=index)
prediction = model(result.observation.image_base64)
result = env.step(LatexOCRAction(latex=prediction))
<span class="hljs-built_in">print</span>(index, result.reward)<!----></pre></div><!----> <p>Or with <code>curl</code>, against a locally running server:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg><!----> <div class=" absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0 "><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent;"></div> Copied</div><!----></button><!----></div> <pre class="language-bash "><!---->curl http://localhost:8000/list_environments
curl http://localhost:8000/latex_ocr_env/splits
curl -X POST http://localhost:8000/latex_ocr_env/num_tasks \
-H <span class="hljs-string">&#x27;Content-Type: application/json&#x27;</span> -d <span class="hljs-string">&#x27;{&quot;split&quot;: &quot;test&quot;}&#x27;</span>
curl -X POST http://localhost:8000/latex_ocr_env/task_range \
-H <span class="hljs-string">&#x27;Content-Type: application/json&#x27;</span> -d <span class="hljs-string">&#x27;{&quot;split&quot;: &quot;test&quot;, &quot;start&quot;: 0, &quot;stop&quot;: 4}&#x27;</span><!----></pre></div><!----> <!--[1--><h2 class="relative group"><a id="imported-environments" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#imported-environments"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Imported Environments</span></h2><!--]--><!----> <p><code>openenv import</code> generates wrappers that already implement the Task API by delegating to the source environment’s own task and split methods, and whose <code>reset()</code> accepts <code>split</code> / <code>index</code> / <code>task_spec</code>. If you are bringing in an ORS/OpenReward or Prime Intellect Verifiers environment, you get task discovery without writing any of the above — see the <a href="../reference/cli">CLI reference</a> for <code>openenv import</code>.</p> <!--[1--><h2 class="relative group"><a id="checklist" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#checklist"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Checklist</span></h2><!--]--><!----> <p>Before shipping a dataset-backed environment:</p> <ol><li>All five methods defined, or a deliberate <code>NotImplementedError</code> for the ones you cannot support.</li> <li>Every method works on a fresh instance, with no reliance on <code>reset()</code> having run.</li> <li>No side effects — no cursor advanced, no stream consumed, no episode state mutated.</li> <li><code>env_name</code> passed explicitly to <code>create_app()</code>.</li> <li><code>get_task()</code> raises <code>IndexError</code> for out-of-range indices.</li> <li><code>num_tasks()</code> reports the honest total, even if <code>list_tasks()</code> truncates.</li> <li><code>reset()</code> accepts <code>split</code> and <code>index</code> with the same names the trainer will use, and falls back to <code>seed</code>-driven selection when <code>index</code> is omitted.</li></ol> <!--[1--><h2 class="relative group"><a id="related-reading" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#related-reading"><span><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg><!----></span></a> <span>Related Reading</span></h2><!--]--><!----> <ul><li><a href="concepts">Concepts</a></li> <li><a href="rl-integration">RL Training</a></li> <li><a href="../reference/core">Core API</a></li> <li><a href="../reference/cli">CLI reference</a></li></ul> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/huggingface/openenv/blob/main/docs/source/guides/task-api.md" target="_blank"><svg class="mr-1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M31,16l-7,7l-1.41-1.41L28.17,16l-5.58-5.59L24,9l7,7z"></path><path d="M1,16l7-7l1.41,1.41L3.83,16l5.58,5.59L8,23l-7-7z"></path><path d="M12.419,25.484L17.639,6.552l1.932,0.518L14.351,26.002z"></path></svg><!----> <span><span class="underline">Update</span> on GitHub</span></a><!----> <p></p><!--]--><!----><!--]--><!--]--><!--]--> <!--[-1--><!--]--><!--]-->
<script>
{
__sveltekit_1pqjjsn = {
base: "/docs/openenv/main/en",
assets: "/docs/openenv/main/en"
};
const element = document.currentScript.parentElement;
Promise.all([
import("/docs/openenv/main/en/_app/immutable/entry/start.B8Noo2MH.js"),
import("/docs/openenv/main/en/_app/immutable/entry/app.CKFy4Q6n.js")
]).then(([kit, app]) => {
kit.start(app, element, {
node_ids: [0, 23],
data: [null,null],
form: null,
error: null
});
});
}
</script>

Xet Storage Details

Size:
61.7 kB
·
Xet hash:
259bacd3e441686a0aec262d4a8ee9d45bef4ad0e1057a92714695b43a65e4d1

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.