Skip to main content
Learn how to build clients that interact with ORS servers for training and evaluating agents.

Two Approaches

Option 1: Use ORS Python SDK

Simplest approach - handles all protocol details:
from ors.client import ORS

client = ORS(base_url="http://localhost:8080")
env = client.environment("myenv")

# Run episode
with env.session(task=task) as session:
    prompt = session.get_prompt()
    result = session.call_tool("submit", {"answer": "42"})

Option 2: Custom HTTP Client

For other languages - implement HTTP protocol:

Python HTTP Client Example

import httpx
import json

class ORSClient:
    def __init__(self, base_url: str):
        self.base_url = base_url
        self.client = httpx.Client(timeout=60.0)

    def list_tools(self, env_name: str):
        """Get available tools"""
        response = self.client.get(f"{self.base_url}/{env_name}/tools")
        response.raise_for_status()
        return response.json()["tools"]

    def list_tasks(self, env_name: str, split: str):
        """Get tasks for split"""
        response = self.client.post(
            f"{self.base_url}/{env_name}/tasks",
            json={"split": split}
        )
        response.raise_for_status()
        return response.json()["tasks"]

    def create_session(self):
        """Generate session ID"""
        response = self.client.post(f"{self.base_url}/create_session")
        response.raise_for_status()
        return response.json()["sid"]

    def create_episode(self, session_id: str, env_name: str, task_spec: dict):
        """Create episode instance"""
        response = self.client.post(
            f"{self.base_url}/create",
            headers={"X-Session-ID": session_id},
            json={
                "env_name": env_name,
                "task_spec": task_spec,
                "secrets": {}
            }
        )
        response.raise_for_status()
        return response.json()

    def get_prompt(self, session_id: str, env_name: str):
        """Get initial prompt"""
        response = self.client.get(
            f"{self.base_url}/{env_name}/prompt",
            headers={"X-Session-ID": session_id}
        )
        response.raise_for_status()
        return response.json()

    def call_tool(self, session_id: str, env_name: str, tool_name: str, tool_input: dict):
        """Call tool (handles SSE)"""
        with self.client.stream(
            "POST",
            f"{self.base_url}/{env_name}/call",
            headers={
                "X-Session-ID": session_id,
                "Accept": "text/event-stream"
            },
            json={"name": tool_name, "input": tool_input}
        ) as response:
            response.raise_for_status()
            buffer = ""
            event_type = ""

            for line in response.iter_lines():
                if line.startswith("event: "):
                    event_type = line[7:]
                elif line.startswith("data: "):
                    data = line[6:]

                    if event_type == "end":
                        complete_data = buffer + data
                        result = json.loads(complete_data)

                        if result["ok"]:
                            return result["output"]
                        else:
                            raise Exception(result["error"])

                    elif event_type == "chunk":
                        buffer += data

                    elif event_type == "error":
                        raise Exception(data)

    def ping(self, session_id: str):
        """Keep session alive (prevents 15-minute timeout)"""
        response = self.client.post(
            f"{self.base_url}/ping",
            headers={"X-Session-ID": session_id}
        )
        response.raise_for_status()
        return response.json()

    def delete_episode(self, session_id: str):
        """Clean up episode"""
        response = self.client.post(
            f"{self.base_url}/delete",
            headers={"X-Session-ID": session_id}
        )
        response.raise_for_status()
        return response.json()

# Usage
client = ORSClient("http://localhost:8080")

# List available tools
tools = client.list_tools("math")
print(f"Available tools: {[t['name'] for t in tools]}")

# Get tasks
tasks = client.list_tasks("math", "train")

# Run episode
task = tasks[0]
session_id = client.create_session()

try:
    client.create_episode(session_id, "math", task)

    prompt = client.get_prompt(session_id, "math")
    print(f"Prompt: {prompt[0]['text']}")

    result = client.call_tool(session_id, "math", "submit", {"answer": "4"})
    print(f"Reward: {result['reward']}")
    print(f"Finished: {result['finished']}")

finally:
    client.delete_episode(session_id)
Session Timeouts: ORS sessions expire after 15 minutes of inactivity. For long-running episodes, call client.ping(session_id) periodically to keep the session alive. The reference SDK pings automatically every 10 seconds.

JavaScript/TypeScript Client

class ORSClient {
  constructor(private baseUrl: string) {}

  async listTools(envName: string) {
    const response = await fetch(`${this.baseUrl}/${envName}/tools`);
    const data = await response.json();
    return data.tools;
  }

  async createSession(): Promise<string> {
    const response = await fetch(`${this.baseUrl}/create_session`, {
      method: 'POST'
    });
    const data = await response.json();
    return data.sid;
  }

  async callTool(
    sessionId: string,
    envName: string,
    toolName: string,
    toolInput: any
  ) {
    const response = await fetch(`${this.baseUrl}/${envName}/call`, {
      method: 'POST',
      headers: {
        'X-Session-ID': sessionId,
        'Accept': 'text/event-stream',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ name: toolName, input: toolInput })
    });

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';
    let eventType = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      const chunk = decoder.decode(value);
      const lines = chunk.split(/\r?\n/);

      for (const line of lines) {
        if (line.startsWith('event: ')) {
          eventType = line.slice(7).trim();
        } else if (line.startsWith('data: ')) {
          const data = line.slice(6).trim();

          if (eventType === 'end') {
            const result = JSON.parse(buffer + data);
            if (result.ok) {
              return result.output;
            } else {
              throw new Error(result.error);
            }
          } else if (eventType === 'chunk') {
            buffer += data;
          } else if (eventType === 'error') {
            throw new Error(data);
          }
        }
      }
    }
  }
}

// Usage
const client = new ORSClient('http://localhost:8080');

const sessionId = await client.createSession();
// ... create episode, call tools

Next Steps

HTTP API

Complete endpoint documentation

Quick Start

See complete client example

Key Takeaway: Building an ORS client is straightforward HTTP programming. Handle SSE for tool calls, manage session IDs, and implement proper error handling. Use the Python SDK for quick development or implement custom clients in any language.