> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ironlabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Router

> Train and deploy your own AI model router to intelligently select the best model for each request.

IronLabs Custom Router lets you train a personalized model selection system on your own data. The router learns from your examples to automatically pick the most appropriate AI model for each task — improving accuracy and reducing costs.

<CardGroup cols={2}>
  <Card title="Python SDK" icon="python" href="https://pypi.org/project/ironlabs/">
    `pip install ironlabs`
  </Card>

  <Card title="Node.js SDK" icon="node-js" href="https://www.npmjs.com/package/ironlabs">
    `npm install ironlabs`
  </Card>
</CardGroup>

## When to use Custom Router

* **Cost optimization** — route simple tasks to cheaper models, reserve powerful ones for complex requests
* **Domain specialization** — match prompts to models that excel in your domain (code, legal, creative writing, etc.)
* **Multi-model pipelines** — let the router decide which model handles each stage instead of hardcoding choices
* **Replace guesswork** — use a data-driven system trained on your own examples instead of manual model selection

## Prerequisites

<Info>
  Before you start, make sure you have:

  * An IronLabs API key from the [Settings page](https://app.ironlabs.ai)
  * A training data file hosted at a publicly accessible URL (GitHub, S3, CDN, etc.)
</Info>

## Installation

Install the SDK for your language:

<CodeGroup>
  ```bash Python theme={null}
  pip install ironlabs
  ```

  ```bash Node.js theme={null}
  npm install ironlabs
  ```
</CodeGroup>

## Initialize the client

Set your API key as an environment variable:

```bash theme={null}
export IRONLABS_API_KEY="your_api_key_here"
```

Then initialize the trainer in your code:

<CodeGroup>
  ```Python Python theme={null}
  from ironlabs import RouterTrainer

  trainer = RouterTrainer()
  ```

  ```javascript Node.js theme={null}
  const { RouterTrainer } = require('ironlabs');

  const trainer = new RouterTrainer();
  ```
</CodeGroup>

<Tip>
  The client automatically picks up `IRONLABS_API_KEY` from your environment — no need to pass it explicitly.
</Tip>

***

## Training a Custom Router

<Steps>
  <Step title="Prepare training data">
    Training data maps prompts to the ideal models for each. Supported formats are JSON and CSV — host your file at any publicly accessible URL (GitHub, S3, CDN, etc.).

    <CodeGroup>
      ```json JSON theme={null}
      [
        {
          "problem": "Write a simple hello world function",
          "correct_models": ["openai/gpt-4o-mini", "anthropic/claude-3-5-haiku-20241022"]
        },
        {
          "problem": "Explain quantum computing in detail",
          "correct_models": ["openai/gpt-4o", "anthropic/claude-3-5-sonnet-20241022"]
        }
      ]
      ```

      ```csv CSV theme={null}
      problem,correct_models
      "Write a simple hello world function","openai/gpt-4o-mini,anthropic/claude-3-5-haiku-20241022"
      "Explain quantum computing in detail","openai/gpt-4o,anthropic/claude-3-5-sonnet-20241022"
      ```
    </CodeGroup>

    <Tip>
      You can pass multiple file URLs to combine datasets from different sources.
    </Tip>
  </Step>

  <Step title="Start training">
    Pass one or more data URLs to kick off a training job.

    <CodeGroup>
      ```Python Python theme={null}
      import time
      from ironlabs import RouterTrainer

      trainer = RouterTrainer()

      data_urls = ["https://example.com/path/to/your/training_data.json"]

      training_info = trainer.fit(data_urls)
      job_id = training_info.get("training_job_id")
      print(f"Training job started. Job ID: {job_id}")
      ```

      ```javascript Node.js theme={null}
      const { RouterTrainer } = require('ironlabs');

      const trainer = new RouterTrainer();

      const dataUrls = ['https://example.com/path/to/your/training_data.json'];

      const trainingJob = await trainer.fit(dataUrls);
      console.log(`Training job started: ${trainingJob.training_job_id}`);
      console.log(`Initial status: ${trainingJob.status}`);
      ```

      ```bash cURL theme={null}
      curl -X POST https://irona-ai--train.modal.run \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -d '{
          "Data_URLs": ["https://example.com/training-data.json"]
        }'
      ```
    </CodeGroup>

    **Response:**

    ```json theme={null}
    {
      "training_job_id": "abc123-def456-ghi789",
      "status": "queued",
      "version": "v1.0"
    }
    ```
  </Step>

  <Step title="Check training status">
    Poll until the job reaches `completed` or `failed`. Training typically takes a few minutes.

    <CodeGroup>
      ```Python Python theme={null}
      while True:
          status_info = trainer.get_status()
          status = status_info.get("status")
          print(f"Current status: {status}")

          if status == "completed":
              model_id = status_info.get("model_id")
              print(f"Training completed! Model ID: {model_id}")
              break
          elif status == "failed":
              print("Training failed.")
              break

          time.sleep(10)
      ```

      ```javascript Node.js theme={null}
      async function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }

      let status = trainingJob.status;

      while (status !== 'completed' && status !== 'failed') {
        console.log(`Current status: ${status}, waiting...`);
        await sleep(10000);

        const statusResponse = await trainer.getStatus();
        status = statusResponse.status;

        if (statusResponse.model_id) {
          console.log(`Model ID: ${statusResponse.model_id}`);
        }
      }

      if (status === 'failed') {
        console.error('Training failed!');
        process.exit(1);
      }
      ```

      ```bash cURL theme={null}
      curl -X GET "https://irona-ai--taskstatus.modal.run?task_id=abc123-def456-ghi789" \
        -H "Authorization: Bearer YOUR_API_KEY"
      ```
    </CodeGroup>

    **Response:**

    ```json theme={null}
    {
      "training_job_id": "abc123-def456-ghi789",
      "status": "completed",
      "model_id": "model-xyz-top1_0.8542",
      "started_at": "2026-02-04T10:30:00Z",
      "completed_at": "2026-02-04T10:35:00Z",
      "training_config": {
        "timing": {
          "training_time_seconds": 45.2,
          "embedding_time_seconds": 120.5,
          "total_time_seconds": 300.0
        }
      }
    }
    ```

    | Status      | Description                    |
    | ----------- | ------------------------------ |
    | `queued`    | Job is waiting to start        |
    | `running`   | Training is in progress        |
    | `completed` | Training finished successfully |
    | `failed`    | Training encountered an error  |
  </Step>

  <Step title="Get model details">
    Retrieve metadata and performance metrics for your trained model.

    <CodeGroup>
      ```Python Python theme={null}
      details = trainer.get_model_details()
      print(f"Model details: {details}")
      ```

      ```javascript Node.js theme={null}
      const modelDetails = await trainer.getModelDetails();

      console.log(`
      Model Information:
        ID: ${modelDetails.model_id}
        Version: ${modelDetails.version}
        Status: ${modelDetails.status}
        Embedding Model: ${modelDetails.embedding_model}
        Number of Classes: ${modelDetails.num_classes}
        Created: ${modelDetails.created_at}
      `);

      if (modelDetails.metrics) {
        console.log('Performance Metrics:');
        console.log(JSON.stringify(modelDetails.metrics, null, 2));
      }
      ```

      ```bash cURL theme={null}
      curl -X GET "https://irona-ai--models.modal.run?model_id=model-xyz-top1_0.8542" \
        -H "Authorization: Bearer YOUR_API_KEY"
      ```
    </CodeGroup>

    **Response:**

    ```json theme={null}
    {
      "model_id": "model-xyz-top1_0.8542",
      "version": "v1.0",
      "status": "active",
      "embedding_model": "Qwen/Qwen3-Embedding-4B",
      "num_classes": 8,
      "created_at": "2026-02-04T10:35:00Z",
      "metrics": {
        "top1_hit_rate": 0.8542,
        "top3_hit_rate": 0.9521,
        "accuracy": 0.8542
      }
    }
    ```
  </Step>

  <Step title="Run inference">
    Use your trained router to select the best model for new prompts.

    <CodeGroup>
      ```Python Python theme={null}
      inputs = [
          "Write a Python function to calculate fibonacci numbers",
          "How to implement a binary search tree in JavaScript?",
          "What is the time complexity of quicksort?"
      ]

      predictions = trainer.predict(inputs)

      for i, pred in enumerate(predictions.get("predictions", [])):
          print(f"Input: {inputs[i]}")
          print(f"Recommended Model: {pred.get('top_model')}")
          print(f"Confidence: {pred.get('top_prob'):.0%}")
          print("-" * 40)
      ```

      ```javascript Node.js theme={null}
      const inputs = [
        'Write a Python function to calculate fibonacci numbers',
        'How to implement a binary search tree in JavaScript?',
        'What is the time complexity of quicksort?',
      ];

      const result = await trainer.predict(inputs);

      result.predictions.forEach((pred, i) => {
        console.log(`Input: ${inputs[i]}`);
        console.log(`Top Model: ${pred.top_model}`);
        console.log(`Confidence: ${(pred.top_prob * 100).toFixed(2)}%`);

        if (pred.models && pred.models.length > 1) {
          console.log('Top 3 Recommendations:');
          pred.models.slice(0, 3).forEach((m, idx) => {
            console.log(`  ${idx + 1}. ${m.model} - ${(m.confidence * 100).toFixed(2)}% (rank ${m.rank})`);
          });
        }
        console.log('---');
      });
      ```

      ```bash cURL theme={null}
      curl -X POST https://irona-ai--infer.modal.run \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer YOUR_API_KEY" \
        -d '{
          "model_id": "model-xyz-top1_0.8542",
          "inputs": [
            "Write a Python function to calculate fibonacci numbers",
            "Explain the theory of relativity"
          ]
        }'
      ```
    </CodeGroup>

    **Response:**

    ```json theme={null}
    {
      "predictions": [
        {
          "top_model": "openai/gpt-4o-mini",
          "top_prob": 0.92,
          "models": [
            { "model": "openai/gpt-4o-mini", "confidence": 0.92, "rank": 1 },
            { "model": "anthropic/claude-3-5-haiku-20241022", "confidence": 0.06, "rank": 2 }
          ]
        }
      ],
      "version": "v1.0"
    }
    ```

    Each prediction includes:

    * `top_model` — the recommended model for this input
    * `top_prob` — confidence score (0–1)
    * `models` — full ranked list with individual confidence scores
  </Step>
</Steps>

***

## Complete example

<Accordion title="View full end-to-end example">
  <CodeGroup>
    ```Python Python theme={null}
    import time
    from ironlabs import RouterTrainer

    def main():
        trainer = RouterTrainer()

        # 1. Start training
        data_urls = ["https://example.com/path/to/your/training_data.json"]
        training_info = trainer.fit(data_urls)
        job_id = training_info.get("training_job_id")
        print(f"Training job started. Job ID: {job_id}")

        # 2. Poll until complete
        while True:
            status_info = trainer.get_status()
            status = status_info.get("status")
            print(f"Current status: {status}")

            if status == "completed":
                model_id = status_info.get("model_id")
                print(f"Training completed! Model ID: {model_id}")
                break
            elif status == "failed":
                print("Training failed.")
                return

            time.sleep(10)

        # 3. Inspect model
        details = trainer.get_model_details()
        print(f"Model details: {details}")

        # 4. Run inference
        test_inputs = [
            "Write a Python function to calculate fibonacci numbers",
            "How to implement a binary search tree in JavaScript?",
            "What is the time complexity of quicksort?"
        ]

        predictions = trainer.predict(test_inputs)

        for i, pred in enumerate(predictions.get("predictions", [])):
            print(f"Input: {test_inputs[i]}")
            print(f"Recommended Model: {pred.get('top_model')}")
            print(f"Confidence: {pred.get('top_prob'):.0%}")
            print("-" * 40)

    if __name__ == "__main__":
        main()
    ```

    ```javascript Node.js theme={null}
    const { RouterTrainer } = require('ironlabs');

    async function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

    async function main() {
      const trainer = new RouterTrainer();

      // 1. Start training
      const dataUrls = ['https://api.npoint.io/1181b4ec6c6333632b49'];
      const trainingJob = await trainer.fit(dataUrls);
      console.log(`Training job started: ${trainingJob.training_job_id}`);

      // 2. Poll until complete
      let status = trainingJob.status;
      while (status !== 'completed' && status !== 'failed') {
        console.log(`Status: ${status}, waiting...`);
        await sleep(10000);
        const statusResponse = await trainer.getStatus();
        status = statusResponse.status;
        if (statusResponse.model_id) {
          console.log(`Model ID: ${statusResponse.model_id}`);
        }
      }

      if (status === 'failed') {
        console.error('Training failed!');
        process.exit(1);
      }

      // 3. Inspect model
      const modelDetails = await trainer.getModelDetails();
      console.log(`Model: ${modelDetails.model_id} | Classes: ${modelDetails.num_classes}`);
      if (modelDetails.metrics) {
        console.log('Metrics:', JSON.stringify(modelDetails.metrics, null, 2));
      }

      // 4. Run inference
      const inputs = [
        'Write a Python function to calculate the factorial of a number',
        'Explain the concept of machine learning',
        'What is the difference between REST and GraphQL?',
      ];

      const result = await trainer.predict(inputs);

      result.predictions.forEach((pred, i) => {
        console.log(`\nInput: ${inputs[i]}`);
        console.log(`Top Model: ${pred.top_model} (${(pred.top_prob * 100).toFixed(2)}%)`);
        if (pred.models && pred.models.length > 1) {
          const alts = pred.models.slice(1, 3).map(m => m.model).join(', ');
          console.log(`Alternatives: ${alts}`);
        }
      });
    }

    main().catch(error => {
      console.error('Error:', error);
      process.exit(1);
    });
    ```
  </CodeGroup>
</Accordion>

***

## Loading an existing model

Reuse a previously trained model without going through training again.

<CodeGroup>
  ```Python Python theme={null}
  from ironlabs import RouterTrainer

  trainer = RouterTrainer()
  trainer.model_id = "model-xyz-top1_0.8542"

  predictions = trainer.predict(["What is the time complexity of quicksort?"])
  print(f"Recommended model: {predictions['predictions'][0]['top_model']}")
  ```

  ```javascript Node.js theme={null}
  const { RouterTrainer } = require('ironlabs');

  const trainer = new RouterTrainer();
  trainer.setModelId('model-xyz-top1_0.8542');

  const result = await trainer.predict(['What is the time complexity of quicksort?']);
  console.log(`Recommended model: ${result.predictions[0].top_model}`);
  ```
</CodeGroup>

***

## Batch processing

The predict endpoint supports up to **500 inputs per request**. For larger datasets, split into chunks.

<CodeGroup>
  ```Python Python theme={null}
  from ironlabs import RouterTrainer

  trainer = RouterTrainer()
  trainer.model_id = "model-xyz-top1_0.8542"

  large_dataset = [f"Test query {i}" for i in range(1200)]
  BATCH_SIZE = 500

  all_predictions = []
  for i in range(0, len(large_dataset), BATCH_SIZE):
      batch = large_dataset[i:i + BATCH_SIZE]
      result = trainer.predict(batch)
      all_predictions.extend(result.get("predictions", []))
      print(f"Processed {min(i + BATCH_SIZE, len(large_dataset))}/{len(large_dataset)} inputs")

  print(f"Total predictions: {len(all_predictions)}")
  ```

  ```javascript Node.js theme={null}
  const { RouterTrainer } = require('ironlabs');

  const trainer = new RouterTrainer();
  trainer.setModelId('model-xyz-top1_0.8542');

  const largeDataset = Array.from({ length: 1200 }, (_, i) => `Test query ${i + 1}`);
  const BATCH_SIZE = 500;

  const allPredictions = [];
  for (let i = 0; i < largeDataset.length; i += BATCH_SIZE) {
    const batch = largeDataset.slice(i, i + BATCH_SIZE);
    const batchNum = Math.floor(i / BATCH_SIZE) + 1;
    const totalBatches = Math.ceil(largeDataset.length / BATCH_SIZE);
    console.log(`Processing batch ${batchNum}/${totalBatches}...`);

    const result = await trainer.predict(batch);
    allPredictions.push(...result.predictions);
    console.log(`  Processed ${Math.min(i + BATCH_SIZE, largeDataset.length)}/${largeDataset.length}`);
  }

  console.log(`Total predictions: ${allPredictions.length}`);
  ```
</CodeGroup>

***

## Supported data formats

| Format             | Description                                                                               |
| ------------------ | ----------------------------------------------------------------------------------------- |
| **JSON**           | Array of objects with `problem` and `correct_models` fields                               |
| **CSV**            | Columns for `problem` and `correct_models` (comma-separated model names in a single cell) |
| **Multiple files** | Pass multiple URLs in `Data_URLs` to combine datasets                                     |

## Model lifecycle

| State        | Description                                               |
| ------------ | --------------------------------------------------------- |
| **Active**   | Available for inference                                   |
| **Archived** | Automatically archived after 1 year of inactivity         |
| **Removed**  | Inactive models are cleaned up weekly to optimize storage |
