# API keys Source: https://docs.primeintellect.ai/api-reference/api-keys How to generate and use authentication keys with our API ## Overview This guide explains how to generate and manage API keys for authenticating requests to our API. Proper use of API keys is critical for securing your application and controlling access. ## Generating an API Key To generate API Key, navigate to [Settings -> API Keys](https://app.primeintellect.ai/dashboard/tokens), and click the `Generate New Key +` button. ![create key](https://mintcdn.com/primeintellect/u4gd9w_CirhPniY2/images/api/keys/create-key.png?fit=max\&auto=format\&n=u4gd9w_CirhPniY2\&q=85\&s=47b043c74405eb713304b4db281ede9e) You can assign fine-grained permissions to each key, allowing you to create multiple keys with specific scopes or roles. This enhances the security of your application by limiting each key's responsibilities. Additionally, you can set an expiration date for each key. We strongly recommend **always** setting an expiration date to avoid using indefinitely valid keys. > [!WARNING] > > Ensure that all API keys are stored securely and never shared in untrusted environments or with third-party applications. Once a key is generated, you will be able to copy it for immediate use. ![copy key](https://mintcdn.com/primeintellect/u4gd9w_CirhPniY2/images/api/keys/new-key.png?fit=max\&auto=format\&n=u4gd9w_CirhPniY2\&q=85\&s=b8a3ccc3cdc7d9bc72456634ab801588) > [!WARNING] > > The key value is displayed only once. If you lose it, you will not be able to retrieve it again. ## Revoking an API Key If you no longer need a particular API key go to [Settings -> API Keys](https://app.primeintellect.ai/dashboard/tokens), find your key on the list and click on `Remove` button on the right side. ## Prime Traces permissions For Traces SDK and CLI operations, create a key with the required Prime Traces read/write permissions. Confirm the personal or team workspace as well as the key permissions. See [Traces authentication and workspace selection](/traces/uploading#authentication-and-workspace). # Delete Token Source: https://docs.primeintellect.ai/api-reference/api-keys/delete-token https://api.primeintellect.ai/openapi.json delete /api/v1/tokens/{token_id} # Disable Token Source: https://docs.primeintellect.ai/api-reference/api-keys/disable-token https://api.primeintellect.ai/openapi.json post /api/v1/tokens/{token_id}/disable Disable an API key. Re-enabling is dashboard-only, so a leaked key cannot turn back on keys its owner disabled. # List Tokens Source: https://docs.primeintellect.ai/api-reference/api-keys/list-tokens https://api.primeintellect.ai/openapi.json get /api/v1/tokens/ # Get Disks Availability Source: https://docs.primeintellect.ai/api-reference/availability/get-disks-availability https://api.primeintellect.ai/openapi.json get /api/v1/availability/disks # Get Gpu Availability Source: https://docs.primeintellect.ai/api-reference/availability/get-gpu-availability https://api.primeintellect.ai/openapi.json get /api/v1/availability/gpus # Get Gpu Summary Source: https://docs.primeintellect.ai/api-reference/availability/get-gpu-summary https://api.primeintellect.ai/openapi.json get /api/v1/availability/gpu-summary Get GPU pricing summary data grouped by GPU type and instance count. # Get Legacy Cluster Availability Source: https://docs.primeintellect.ai/api-reference/availability/get-legacy-cluster-availability https://api.primeintellect.ai/openapi.json get /api/v1/availability/clusters # Get Legacygpu Availability Source: https://docs.primeintellect.ai/api-reference/availability/get-legacygpu-availability https://api.primeintellect.ai/openapi.json get /api/v1/availability/ # Get Multinode Availability Source: https://docs.primeintellect.ai/api-reference/availability/get-multinode-availability https://api.primeintellect.ai/openapi.json get /api/v1/availability/multi-node # Get Multinode Summary Source: https://docs.primeintellect.ai/api-reference/availability/get-multinode-summary https://api.primeintellect.ai/openapi.json get /api/v1/availability/multi-node-summary Get multi-node (cluster) pricing summary data grouped by GPU type and instance count. # Token usage and cost for a single RFT run Source: https://docs.primeintellect.ai/api-reference/billing/token-usage-and-cost-for-a-single-rft-run https://api.primeintellect.ai/openapi.json get /api/v1/billing/runs/{run_id}/usage Return the running token + cost totals for one RFT run. Mirrors the per-row training data shown on the billing page (Training tokens, Inference input/output tokens, price per million tokens, and total cost) so an agent can poll this endpoint to monitor a live run. # Wallet balance + most recent billing rows Source: https://docs.primeintellect.ai/api-reference/billing/wallet-balance-+-most-recent-billing-rows https://api.primeintellect.ai/openapi.json get /api/v1/billing/wallet Return the wallet's current balance and most recent billing rows. All resource types are included (compute, training, inference, traces, disks, sandboxes, images) — same source-of-truth `Billing` table the dashboard's Billing History tab reads. Sorted by `lastBilledAt` desc. # Get Availability Information Source: https://docs.primeintellect.ai/api-reference/check-gpu-availability How to check GPU, cluster, and disk availability and pricing > [!NOTE] > > Before you start, ensure that you have [API Key](./api-keys) with `Availability -> Read` permission ## Available Endpoints The availability API provides two endpoints to query different types of resources: * **`/api/v1/availability/gpus`** - Get GPU instance availability * **`/api/v1/availability/disks`** - Get standalone disk availability ## Retrieving GPU Availability Data Suppose you want to check pricing options for a single **H100 GPU**, with the location restricted to the **United States** or **Canada**. To do this, send a request to our availability endpoint as shown below: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/gpus?regions=united_states®ions=canada&gpu_count=1&gpu_type=H100_80GB' \ --header 'Authorization: Bearer your_api_key' ``` > [!TIP] > > You can generate request samples using our interactive [`Availability API`](./availability/get-gpu-availability) > documentation This is a **GET** request, and it requires an **Authorization: Bearer** token with your API key. The request accepts query parameters, allowing you to filter GPUs by region, GPU count, GPU type, and other criteria. In this case, the filters include **regions**, **gpu\_count**, and **gpu\_type**. ### Query Parameters All availability endpoints support pagination and the following filters: | Parameter | Type | Description | | - | - | - | | `regions` | List\[string] | Filter by region(s) (e.g., `united_states`, `canada`, `europe_west`) | | `gpu_count` | integer | Desired number of GPUs | | `gpu_type` | string | GPU model (e.g., `H100_80GB`, `A100_80GB`) | | `socket` | string | GPU socket type (e.g., `PCIe`, `SXM`) | | `security` | string | Security type: `secure_cloud` | | `data_center_id` | string | Filter by specific data center ID | | `cloud_id` | string | Filter by provider's cloud ID | | `disks` | List\[string] | Filter by disk IDs (see [Disk Filtering](#filtering-by-user-disks) section) | | `page` | integer | Page number (default: 1, min: 1) | | `page_size` | integer | Results per page (default: 100, max: 100) | ## Understanding the GPU Response Object The GPU availability endpoint returns a paginated response with a list of available GPU configurations and the total count. Here is an example of the JSON response you might receive: ```json Example response theme={null} { "items": [ { "cloudId": "NVIDIA H100 PCIe", "gpuType": "H100_80GB", "socket": "PCIe", "provider": "runpod", "region": "united_states", "dataCenter": "US-KS-2", "country": "US", "gpuCount": 1, "gpuMemory": 80, "disk": { "minCount": 80, "defaultCount": 80, "maxCount": 1000, "pricePerUnit": 0.00014, "step": 1, "defaultIncludedInPrice": false, "additionalInfo": null }, "vcpu": { "defaultCount": 16 }, "memory": { "defaultCount": 251 }, "internetSpeed": null, "interconnect": null, "interconnectType": null, "provisioningTime": null, "stockStatus": "Low", "security": "secure_cloud", "prices": { "onDemand": 2.69, "isVariable": false, "currency": "USD" }, "images": [ "ubuntu_22_cuda_12", "cuda_12_1_pytorch_2_2", "cuda_11_8_pytorch_2_1", "stable_diffusion", "flux", "axolotl", "bittensor", "vllm_llama_8b", "vllm_llama_70b", "vllm_llama_405b" ], "isSpot": null, "prepaidTime": null } ], "totalCount": 247 } ``` > [!NOTE] > > For a full breakdown of the response schema, see the [`Get Gpu Availability > Endpoint`](./availability/get-gpu-availability) documentation ### Response Structure #### `items` An array containing the GPU availability records for the current page. Each item represents a unique GPU configuration from a provider. #### `totalCount` The total number of GPU configurations matching your filters across all pages. For example, if `totalCount` is 247 and `page_size` is 100, you know there are 3 pages total. Let's walk through the most important fields in each GPU availability item: #### `provider` Indicates the company or platform providing the GPU. You may encounter multiple offers from the same provider. #### `cloudId` A unique identifier provided by the GPU provider. This value is required if you plan to provision the GPU through our provisioning API. #### `region` The geographic region where the GPU is located (e.g., `united_states`, `europe_west`). This helps you select resources closer to your users. #### `dataCenter` Optional but necessary if present in the response. If the provider operates multiple datacenters with the same `cloudId`, you must specify which data center to use when provisioning the GPU. #### Resource Specifications: `disk`, `sharedDisk`, `vcpu`, `memory` Each resource field contains detailed specification information with the following attributes: * **`minCount`**: Minimum allowed value for this resource * **`maxCount`**: Maximum allowed value for this resource * **`defaultCount`**: Default value provided by the provider * **`pricePerUnit`**: Cost per unit per hour (if customizable) * **`step`**: Increment step for adjusting the value * **`defaultIncludedInPrice`**: Whether the default value is included in the base price * **`additionalInfo`**: Extra information about pricing or constraints **`disk`**: Instance-local disk storage in GB. This storage is attached to your GPU instance. **`vcpu`**: Number of virtual CPUs allocated to the instance. **`memory`**: RAM size available in GB. #### Cost Calculation Example In the example above, your server will include `16 vCPUs` and `251GB of RAM`, with customizable `disk` space. If the disk space is adjustable, its cost is calculated separately from the **prices** property. Here's an example breakdown of total costs: ``` GPU cost: $2.69 vcpu cost: $0.00 memory cost: $0.00 disk cost: $0.0112 (80 units * $0.00014) Total cost: $2.7012 ``` The total cost varies based on the disk space that you send to the provisioning API. The value can range between `minCount` and `maxCount` adjustable by `step` and you'll pay `pricePerUnit/hr` for every unit you want to use. Some providers offer predefined server configurations, with the `defaultIncludedInPrice` set to `true`. This means the `defaultCount` is included in the base price, but any changes (even reductions) will incur additional charges. #### `interconnect` and `interconnectType` For multi-GPU setups, these fields specify the interconnect speed (in Gbps) and technology type (e.g., `InfiniBand`, `NVLink`) used for GPU-to-GPU communication. #### `provisioningTime` Estimated time in minutes required to provision and start the GPU instance. #### `prices` and `security` GPUs are provided by providers that maintain security standards and are hosted in secured datacenters. The price is defined in `prices.onDemand`. Some providers may also use dynamic pricing for their instances. If the field `isVariable` is set to `true`, the price may fluctuate based on demand or currency conversion. #### `prepaidTime` If set, you will be pre-charged for the total number of hours specified in this field at the time of ordering, regardless of actual usage. After the prepaid time expires, the standard hourly rate will apply. ## Filtering by User Disks The `disks` query parameter allows you to filter GPU availability based on your existing disks with persisted data. This is particularly useful when you want to provision a GPU instance that can attach to disks where you've already stored your datasets, models, or other important data. ### How It Works When you provide disk IDs via the `disks` parameter, the API: 1. Fetches the disk information for the provided disk IDs 2. Filters GPU availability to show only instances that can be provisioned in locations where your disks are available for attachment 3. Applies rest of the filters to narrow down the results. ### Example Request Suppose you have two disks with IDs `clhxy6aw80000j8080gdf8kqv` and `clhxy9bz50001j8080hdf9lrw` containing your training datasets in different datacenters. You can find GPUs that can attach to either disk: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/gpus?disks=clhxy6aw80000j8080gdf8kqv&disks=clhxy9bz50001j8080hdf9lrw&gpu_type=H100_80GB' \ --header 'Authorization: Bearer your_api_key' ``` This will return H100 GPUs available in the same locations where your disks are stored, making it easy to reuse your persisted data. ### Use Cases * **Data Persistence**: Reuse datasets, models, and checkpoints stored on existing disks * **Quick Provisioning**: Attach pre-loaded data disks to new GPU instances without re-uploading ## Checking Disk Availability The disk availability endpoint allows you to query standalone network-attached storage options across different providers and datacenters. ### Endpoint ```bash theme={null} GET https://api.primeintellect.ai/api/v1/availability/disks ``` ### Example Request ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/disks?regions=united_states&page=1&page_size=50' \ --header 'Authorization: Bearer your_api_key' ``` ### Query Parameters for Disk Availability | Parameter | Type | Description | | - | - | - | | `regions` | List\[string] | Filter by region(s) | | `data_center_id` | string | Filter by specific data center ID | | `cloud_id` | string | Filter by provider's cloud ID | | `page` | integer | Page number (default: 1, min: 1) | | `page_size` | integer | Results per page (default: 100, max: 100) | ### Disk Availability Response The disk endpoint returns a paginated response with disk availability objects and the total count: ```json Example disk response theme={null} { "items": [ { "cloudId": null, "provider": "hyperstack", "dataCenter": "US-1", "country": "US", "region": "united_states", "spec": { "minCount": 0, "defaultCount": 0, "maxCount": 100000, "pricePerUnit": 0.00015, "step": 1, "defaultIncludedInPrice": false, "additionalInfo": null }, "stockStatus": "Available", "security": "secure_cloud", "isMultinode": false } ], "totalCount": 15 } ``` ### Key Fields in Disk Response #### `spec` Contains the disk size specification with the same structure as resource specifications in GPU responses: * **`minCount`**: Minimum disk size in GB * **`maxCount`**: Maximum disk size in GB * **`pricePerUnit`**: Cost per GB per hour * **`step`**: Size increment (usually 1 GB) #### `isMultinode` Indicates whether this disk can be attached to multiple instances simultaneously. This is useful for shared data scenarios where multiple GPU instances need access to the same dataset. #### `security` Disks are hosted on secure cloud infrastructure. ### Disk Cost Calculation Disk costs are straightforward to calculate: ``` Disk cost per hour = disk_size_gb * pricePerUnit ``` For example, a 500GB disk with `pricePerUnit: 0.00015`: ``` 500 GB * $0.00015/GB/hr = $0.075/hr ``` ## Pagination All availability endpoints support pagination to handle large result sets efficiently: * Use `page` to specify which page of results to retrieve (1-indexed) * Use `page_size` to control how many results per page (max 100) * Results are returned in consistent order for predictable pagination Example with pagination: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/gpus?gpu_type=A100_80GB&page=2&page_size=50' \ --header 'Authorization: Bearer your_api_key' ``` # Create Disk Source: https://docs.primeintellect.ai/api-reference/disks/create-disk https://api.primeintellect.ai/openapi.json post /api/v1/disks/ # Delete Disk Source: https://docs.primeintellect.ai/api-reference/disks/delete-disk https://api.primeintellect.ai/openapi.json delete /api/v1/disks/{disk_id} # Get Disk Source: https://docs.primeintellect.ai/api-reference/disks/get-disk https://api.primeintellect.ai/openapi.json get /api/v1/disks/{disk_id} # List Disks Source: https://docs.primeintellect.ai/api-reference/disks/list-disks https://api.primeintellect.ai/openapi.json get /api/v1/disks/ # Update Disk Source: https://docs.primeintellect.ai/api-reference/disks/update-disk https://api.primeintellect.ai/openapi.json patch /api/v1/disks/{disk_id} # Bulk Delete Evaluations Source: https://docs.primeintellect.ai/api-reference/evals/bulk-delete-evaluations https://api.primeintellect.ai/openapi.json post /api/v1/evaluations/bulk-delete # Create Evaluation Source: https://docs.primeintellect.ai/api-reference/evals/create-evaluation https://api.primeintellect.ai/openapi.json post /api/v1/evaluations/ Create a new evaluation This endpoint supports: - Environment evaluations: Provide environments - Prime RL evaluations: Provide run_id - Suite evaluations: Provide suite_id Ownership: - If team_id is provided in request, the evaluation will be owned by the team - Otherwise, the evaluation will be owned by the authenticated user # Delete Evaluation Source: https://docs.primeintellect.ai/api-reference/evals/delete-evaluation https://api.primeintellect.ai/openapi.json delete /api/v1/evaluations/{evaluation_id} # Finalize Evaluation Source: https://docs.primeintellect.ai/api-reference/evals/finalize-evaluation https://api.primeintellect.ai/openapi.json post /api/v1/evaluations/{evaluation_id}/finalize Mark an evaluation as complete and compute final statistics. # Get Evaluation Source: https://docs.primeintellect.ai/api-reference/evals/get-evaluation https://api.primeintellect.ai/openapi.json get /api/v1/evaluations/{evaluation_id} Get detailed information about a specific evaluation. # Get Samples Source: https://docs.primeintellect.ai/api-reference/evals/get-samples https://api.primeintellect.ai/openapi.json get /api/v1/evaluations/{evaluation_id}/samples Get samples for a specific evaluation # List Evaluations Source: https://docs.primeintellect.ai/api-reference/evals/list-evaluations https://api.primeintellect.ai/openapi.json get /api/v1/evaluations/ Get a list of evaluations owned by the authenticated user or a team. Without team_id, returns the user's personal evaluations only. With team_id, returns that team's evaluations; the user must be a member. # Push Samples Source: https://docs.primeintellect.ai/api-reference/evals/push-samples https://api.primeintellect.ai/openapi.json post /api/v1/evaluations/{evaluation_id}/samples Push evaluation samples This endpoint can be called multiple times to stream samples as they're generated. # Update Evaluation Source: https://docs.primeintellect.ai/api-reference/evals/update-evaluation https://api.primeintellect.ai/openapi.json put /api/v1/evaluations/{evaluation_id} Update an existing evaluation # Get Factory Nodes Source: https://docs.primeintellect.ai/api-reference/factory/get-factory-nodes https://api.primeintellect.ai/openapi.json get /api/v1/factory/nodes Per-cluster GPU node tables for the caller's team (the sinfo): node name, coarse state (ready / cordoned / offline / unknown), GPU type and total/used counts, and which allocation the node is assigned to ("slurm" for a claimed node, null otherwise). Node names are the team's own hardware and are shown to the team only; data of other teams is never returned. Unusable capacity snapshots surface as a per-cluster capacity source with an error status, never as a silent empty node list. # Get Factory Status Source: https://docs.primeintellect.ai/api-reference/factory/get-factory-status https://api.primeintellect.ai/openapi.json get /api/v1/factory/status Fleet status for the caller's team: cluster capacity split into training / inference / slurm allocations with reserved, in-use, idle-inside, unassigned and unknown GPU counts plus per-source freshness. The selected `team_id` must be a team the token's user is a member of; data of other teams is never returned. # Get Factory Utilization Source: https://docs.primeintellect.ai/api-reference/factory/get-factory-utilization https://api.primeintellect.ai/openapi.json get /api/v1/factory/utilization The team's Model Factory utilization curve: Slurm node allocated/idle counts per bucket, derived from the slurmdbd accounting record with the live jobs read as the truth for the current bucket. Per-source freshness travels in `sources`; gaps are gaps, never zero-filled. # Get Factory Workloads Source: https://docs.primeintellect.ai/api-reference/factory/get-factory-workloads https://api.primeintellect.ai/openapi.json get /api/v1/factory/workloads The team's Model Factory workloads feed (the squeue): leaf training runs, inference deployments and native Slurm jobs with owner, state, requested/allocated GPUs, per-source freshness and — for terminal states (completed/failed/all) — the completion stamp of the terminal record (`ended_at`), bounded and newest first. The selected `team_id` must be a team the token's user is a member of; data of other teams is never returned. The default state window is running + queued. Windows that include terminal states read the terminal records (RFTRun/Job rows; the slurmdbd accounting read for native Slurm jobs) and are bounded to `limit` rows (default 50, max 200), newest first; `since` bounds them further by timestamp. # Submit Feedback Source: https://docs.primeintellect.ai/api-reference/feedback/submit-feedback https://api.primeintellect.ai/openapi.json post /api/v1/feedback # Cancel Hosted Evaluation Route Source: https://docs.primeintellect.ai/api-reference/hosted-evaluations/cancel-hosted-evaluation-route https://api.primeintellect.ai/openapi.json patch /api/v1/hosted-evaluations/{evaluation_id}/cancel Cancel a running hosted evaluation. # Create Hosted Evaluation Source: https://docs.primeintellect.ai/api-reference/hosted-evaluations/create-hosted-evaluation https://api.primeintellect.ai/openapi.json post /api/v1/hosted-evaluations Create and start a hosted evaluation. # Get Hosted Evaluation Logs Route Source: https://docs.primeintellect.ai/api-reference/hosted-evaluations/get-hosted-evaluation-logs-route https://api.primeintellect.ai/openapi.json get /api/v1/hosted-evaluations/{evaluation_id}/logs Get real-time logs from the sandbox running a hosted evaluation. # Get Inference Models Source: https://docs.primeintellect.ai/api-reference/hosted-evaluations/get-inference-models https://api.primeintellect.ai/openapi.json get /api/v1/hosted-evaluations/models Get available models from Prime Inference API for hosted evaluations # Upload Hosted Evaluation Logs Route Source: https://docs.primeintellect.ai/api-reference/hosted-evaluations/upload-hosted-evaluation-logs-route https://api.primeintellect.ai/openapi.json put /api/v1/hosted-evaluations/{evaluation_id}/logs Store the runner's final log. Called by the hosted-eval sandbox itself as it exits, with the run's own key; a VM sandbox is gone before the exit webhook. # Delete User Image Source: https://docs.primeintellect.ai/api-reference/images/delete-user-image https://api.primeintellect.ai/openapi.json delete /api/v1/images/{image_name}/{image_tag} Delete a user image from the database and Google Artifact Registry. For team images, only the image creator or team admins can delete. ## Rate Limit 100 requests per 60 seconds per IP and token. # Get Build Status Source: https://docs.primeintellect.ai/api-reference/images/get-build-status https://api.primeintellect.ai/openapi.json get /api/v1/images/build/{build_id} Get the status of an image build. # Initiate Image Build Source: https://docs.primeintellect.ai/api-reference/images/initiate-image-build https://api.primeintellect.ai/openapi.json post /api/v1/images/build Build a VM image from a public source or an uploaded Dockerfile context. ## Rate Limit 150 requests per 60 seconds per IP and token. Source builds also have per-image account limits. # List Image Builds Source: https://docs.primeintellect.ai/api-reference/images/list-image-builds https://api.primeintellect.ai/openapi.json get /api/v1/images/builds List all image builds for the current user. # List User Images Source: https://docs.primeintellect.ai/api-reference/images/list-user-images https://api.primeintellect.ai/openapi.json get /api/v1/images List images and builds for the current user, scoped by team context. Returns personal images by default, or team images if teamId is provided. Pagination note: `limit`, `offset`, and `totalCount` count logical images (unique `owner/imageName:imageTag`). A VM image may produce multiple `data[]` rows (the registered artifact plus any active builds), so `len(data)` can exceed `limit` and `totalCount` reflects images, not rows. The optional `search` parameter narrows results (case-insensitively) to images whose name, tag, or `name:tag` reference contains the given substring. Setting `ownerScope=platform` lists only org-less platform images and builds. ## Rate Limit 300 requests per 60 seconds per IP and token. # Start Image Build Source: https://docs.primeintellect.ai/api-reference/images/start-image-build https://api.primeintellect.ai/openapi.json post /api/v1/images/build/{build_id}/start Start the image build process after uploading the build context. Runs img2vm in a VM sandbox to build and convert the image locally. # Update Images Source: https://docs.primeintellect.ai/api-reference/images/update-images https://api.primeintellect.ai/openapi.json patch /api/v1/images Update one or many logical images: visibility, name/tag, and owner. Applies up to 100 independent patches; each item names its source image exactly and may change `visibility`, rename `name`/`tag`, move a personal image into one of the caller's teams, or promote a personal/team image to an org-less platform image (platform admins only; platform images are always PUBLIC). Renames and owner moves update the whole logical group (container + VM artifacts and their linked builds) atomically without moving the backing artifacts: the old reference stops resolving and the new reference resolves to the same content. An owner move settles storage billing to the source wallet at the cutover instant and bills the destination afterwards. Destinations are never overwritten (`destination_exists`). `dryRun` performs resolution, authorization, collision detection, and quota projection without writing; a valid request with item-specific failures still returns 200 with per-item errors. ## Rate Limit 50 requests per 60 seconds per IP and token. # Chat Completions API Source: https://docs.primeintellect.ai/api-reference/inference-chat-completions Generate responses with Prime Inference models Send OpenAI-compatible requests to `POST https://api.pinference.ai/api/v1/chat/completions`. Pass an exact model ID from the [models API](/api-reference/inference-models). ```bash theme={null} export PRIME_API_KEY="your-api-key" curl https://api.pinference.ai/api/v1/chat/completions \ -H "Authorization: Bearer $PRIME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "z-ai/glm-5.3", "messages": [{"role": "user", "content": "Hello"}] }' ``` To charge a team, add `-H "X-Prime-Team-ID: your-team-id"` to each request. See [team billing](/inference/overview#team-billing) for SDK setup and account details. ## Stream a response Set `"stream": true` in the request body to receive server-sent events as tokens are generated. In the OpenAI Python SDK: ```python theme={null} stream = client.chat.completions.create( model="z-ai/glm-5.3", messages=[{"role": "user", "content": "Hello"}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True) ``` ## Usage details If you need token counts and cost in the response, request usage details with the Prime Inference extension: ```python theme={null} response = client.chat.completions.create( model="z-ai/glm-5.3", messages=[{"role": "user", "content": "Hello"}], extra_body={"usage": {"include": True}}, ) print(response.usage) ``` Supported parameters and limits depend on the selected model. Check the [model catalog](https://app.primeintellect.ai/dashboard/inference?tab=models) for current capabilities rather than assuming every model accepts the same options. # Evaluations API Source: https://docs.primeintellect.ai/api-reference/inference-evaluations Score, classify, and answer yes/no questions with evaluation models such as Clef Evaluation models such as [Clef](/inference/models/clef) do not generate text. They read a `state` and answer a set of typed `questions` about it, returning a probability, a choice, or a score for each. Send these requests to `POST https://api.pinference.ai/api/v1/evaluations`; evaluation models are rejected on the [Chat Completions API](/api-reference/inference-chat-completions). ```bash theme={null} export PRIME_API_KEY="your-api-key" curl https://api.pinference.ai/api/v1/evaluations \ -H "Authorization: Bearer $PRIME_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "cloudflare/clef-flash", "state": "User: the checkout page returns a 500 every time I pay.", "questions": { "incident": { "type": "boolean", "instructions": "Is the user reporting an outage?" }, "team": { "type": "choice", "criteria": { "engineering": "Bugs, errors, and outages", "billing": "Charges, refunds, and invoices" } }, "severity": { "type": "score", "criteria": ["minor", "major", "critical"] } } }' ``` ```json theme={null} { "answers": { "incident": {"type": "boolean", "probability": 0.95}, "team": {"type": "choice", "choice": "engineering", "probabilities": {"engineering": 0.97, "billing": 0.03}}, "severity": {"type": "score", "score": 1.5} }, "usage": {"inputTokens": 123, "outputTokens": 0}, "warnings": [] } ``` All questions in a request are answered together against the same `state`. To charge a team, add `-H "X-Prime-Team-ID: your-team-id"` to each request. See [team billing](/inference/overview#team-billing). ## Request body | Field | Type | Description | | - | - | - | | `model` | string | An evaluation model ID from the [models API](/api-reference/inference-models), such as `cloudflare/clef` or `cloudflare/clef-flash`. | | `state` | string, object, or array | The content to evaluate: a conversation, a document, an agent trace, or any JSON value. A state too long for the model's context window (65,536 tokens for Clef, 24,576 for Clef Flash) is truncated to fit. | | `questions` | object | 1 to 64 questions, keyed by an ID you choose. IDs may contain letters, digits, `_`, `.`, and `-`, up to 100 characters. Each answer is returned under the same ID. | | `images` | array of strings | Optional. Up to 4 images as base64 `data:` URLs (PNG, JPEG, or WebP), at most 4 MiB each and 8 MiB in total. Remote image URLs are not accepted. | The whole request body can be at most 16 MiB. ## Question types Every question has a `type` and an optional `instructions` field that tells the model what to judge. `instructions` and the `criteria` descriptions can be strings or JSON values. | Type | `criteria` | Answer fields | | - | - | - | | `boolean` | Optional. An object with `"true"` and/or `"false"` keys that describe what each outcome means. | `probability`: the probability that the answer is true, from 0 to 1. | | `choice` | Required. An object of 1 to 255 options, mapping each option name to a description (or `null`). | `choice`: the selected option name. May include `probabilities` per option. | | `score` | Required. An array of 2 to 10 descriptions, ordered from lowest to highest. | `score`: a number on the scale defined by `criteria`. | Each answer also echoes its `type`. Answers can include additional fields such as `confidence` depending on the model, so read the fields you need and ignore the rest. ## Usage and billing `usage.inputTokens` and `usage.outputTokens` report the tokens processed for the request. Evaluation models are billed on input tokens at the price listed in the [model catalog](https://app.primeintellect.ai/dashboard/inference?tab=models). The response includes an `X-Inference-Id` header you can share with support to trace a request. ## Vercel AI SDK compatibility The endpoint accepts the same request and response format as the Vercel AI Gateway evaluation model protocol, so clients written for that protocol can point at Prime Inference. Prime Inference also serves the gateway's protocol path, `POST https://api.pinference.ai/v4/ai/evaluation-model`, which reads the model ID from the `ai-model-id` request header when the body has no `model` field. # Models API Source: https://docs.primeintellect.ai/api-reference/inference-models List available inference models and inspect their metadata The model catalog is available at `https://api.pinference.ai/api/v1/models`. It includes model IDs, serving types, pricing, and specifications when available. Availability and prices can change; read the current response rather than relying on a fixed list in this guide. ```bash theme={null} export PRIME_API_KEY="your-api-key" curl https://api.pinference.ai/api/v1/models \ -H "Authorization: Bearer $PRIME_API_KEY" ``` To select a team billing account for authenticated requests, add `-H "X-Prime-Team-ID: your-team-id"` (see [team billing](/inference/overview#team-billing)). You can also list models with `prime inference models` or browse the [model catalog](https://app.primeintellect.ai/dashboard/inference?tab=models). Use the returned `id` as the `model` in a [chat completion](/api-reference/inference-chat-completions). For example, the hosted GLM-5.3 model uses `z-ai/glm-5.3`; see the [GLM-5.3 guide](/inference/models/glm-5-3). Evaluation models such as `cloudflare/clef` list `decisions` as their output modality; call them with the [Evaluations API](/api-reference/inference-evaluations) instead. # API Overview Source: https://docs.primeintellect.ai/api-reference/introduction The Prime Intellect API provides programmatic access to our full platform. Get started by [setting up an API Key](./api-keys). ## Inference API Access frontier models via an OpenAI-compatible API. > **[List models](/api-reference/inference-models)** > > View available models and their capabilities > **[Chat completions](/api-reference/inference-chat-completions)** > > Generate text with frontier models ## Compute API Provision and manage GPU instances across providers. > **[Check GPU availability](/api-reference/check-gpu-availability)** > > View GPU resources and availability across providers > **[Provision GPUs](/api-reference/provision-gpu)** > > Deploy GPU instances with customizable configurations > **[Manage instances](/api-reference/managing-pods)** > > Monitor and control your active GPU instances > **[Manage storage](/api-reference/managing-disks)** > > Create persistent network-attached disks ## Traces API Upload, query and search training, evaluation and inference traces. > **[List traces](/api-reference/traces/traces/list-traces)** > > Query trace summaries with filters and cursor pagination > **[Upload traces](/api-reference/traces/ingestion/upload-traces)** > > Upload traces as JSONL in a single multipart request ## Sandboxes API Run code securely in isolated containers. > **[Sandboxes overview](/sandboxes/overview)** > > Learn about sandboxes and their use cases > **[Sandboxes SDK](/sandboxes/sdk)** > > Create and manage sandboxes programmatically # Managing Disks Source: https://docs.primeintellect.ai/api-reference/managing-disks How to create and manage network-attached storage disks > [!NOTE] > > Before you start, ensure that you have [API Key](./api-keys) with `Disks -> Read and write` permission ## Overview Network-attached disks provide persistent storage that can be shared across multiple GPU instances. Disks persist independently from instances, making them ideal for storing datasets, model checkpoints, and other data that needs to survive instance termination. ## Finding Available Disk Options Before creating a disk, use the disk availability endpoint to find available storage options across different providers and datacenters. ### Query Disk Availability ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/disks?regions=united_states&page=1&page_size=50' \ --header 'Authorization: Bearer your_api_key' ``` ### Understanding the Response The response returns available disk configurations: ```json theme={null} [ { "cloudId": null, "provider": "hyperstack", "dataCenter": "US-1", "country": "US", "region": "united_states", "spec": { "minCount": 0, "defaultCount": 0, "maxCount": 100000, "pricePerUnit": 0.00015, "step": 1, "defaultIncludedInPrice": false, "additionalInfo": null }, "stockStatus": "Available", "security": "secure_cloud", "isMultinode": false } ] ``` ### Key Fields * **`cloudId`**: Identifies the storage configuration (if applicable) * **`provider`**: The provider offering the storage (e.g., `hyperstack`, `runpod`) * **`dataCenter`**: Location where the disk will be created * **`spec.minCount`**: Minimum disk size in GB * **`spec.maxCount`**: Maximum disk size in GB * **`spec.pricePerUnit`**: Cost per GB per hour * **`isMultinode`**: Whether the disk can be attached to multiple instances > [!TIP] > > Filter by `regions` or `data_center_id` to find disks in specific locations where you plan to run your GPU instances. ## Creating a Disk Once you've identified a suitable disk configuration from the availability API, create a disk using the information from the availability response. ### Request Body Structure ```json theme={null} { "disk": { "size": 500, "name": "training-dataset", "dataCenterId": "US-1" }, "provider": { "type": "hyperstack" } } ``` ### Parameters | Parameter | Required | Description | | - | - | - | | `disk.size` | Yes | Disk size in GB (must be between `minCount` and `maxCount` from availability) | | `disk.name` | No | Human-friendly name for the disk (auto-generated if not provided) | | `disk.cloudId` | Yes\* | Cloud ID from the availability response (\*if provided in availability response) | | `disk.dataCenterId` | Yes\* | Data center ID from availability (\*if provided in availability response) | | `provider.type` | Yes | Provider type from availability response (e.g., `hyperstack`, `runpod`) | ### Example: Creating a 500GB Disk ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/disks/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "disk": { "size": 500, "name": "ml-training-data", "dataCenterId": "US-1" }, "provider": { "type": "hyperstack" } }' ``` ### Response On successful creation, you'll receive a `201 Created` response with the disk details: ```json theme={null} { "id": "fdb5205fd9b14c9d804d3f70b4c96da0", "name": "ml-training-data", "createdAt": "2025-11-07T21:07:15.183000", "updatedAt": "2025-11-07T21:07:15.183000", "terminatedAt": null, "status": "PROVISIONING", "providerType": "hyperstack", "size": 500, "info": { "cloudId": null, "country": "US", "isMultinode": false, "dataCenterId": "US-1" }, "priceHr": 0.075, "stoppedPriceHr": 0.075, "provisioningPriceHr": 0.0, "userId": "clvb0itli0000oxevorvgrpfn", "teamId": null, "walletId": "clvb0iv0c0004oxev5mnt1xiv", "pods": [], "clusters": [] } ``` ### Cost Calculation The hourly cost is calculated as: ``` Hourly Cost = size (GB) × pricePerUnit (from availability) ``` For a 500GB disk at \$0.00015/GB/hr: ``` 500 GB × $0.00015/GB/hr = $0.075/hr ``` > [!WARNING] > > Disks are billed continuously from creation until termination, regardless of whether they're attached to an instance. ## Listing Your Disks Retrieve all disks associated with your account: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/disks/?limit=50&offset=0' \ --header 'Authorization: Bearer your_api_key' ``` ### Query Parameters | Parameter | Default | Description | | - | - | - | | `limit` | 50 | Number of results per page (max 200) | | `offset` | 0 | Number of results to skip for pagination | ### Response ```json theme={null} { "totalCount": 1, "data": [ { "id": "fdb5205fd9b14c9d804d3f70b4c96da0", "name": "ml-training-data", "createdAt": "2025-11-07T21:07:15.183000", "updatedAt": "2025-11-07T21:08:01.934000", "terminatedAt": null, "status": "ACTIVE", "providerType": "hyperstack", "size": 500, "info": { "cloudId": null, "country": "US", "isMultinode": false, "dataCenterId": "US-1" }, "priceHr": 0.075, "stoppedPriceHr": 0.075, "provisioningPriceHr": 0.0, "userId": "clvb0itli0000oxevorvgrpfn", "teamId": null, "walletId": "clvb0iv0c0004oxev5mnt1xiv", "pods": [], "clusters": [] } ], "limit": 50, "offset": 0 } ``` ## Getting a Single Disk Retrieve detailed information about a specific disk: ```bash theme={null} curl --request GET \ --url https://api.primeintellect.ai/api/v1/disks/fdb5205fd9b14c9d804d3f70b4c96da0 \ --header 'Authorization: Bearer your_api_key' ``` ### Response ```json theme={null} { "id": "fdb5205fd9b14c9d804d3f70b4c96da0", "name": "ml-training-data", "createdAt": "2025-11-07T21:07:15.183000", "updatedAt": "2025-11-07T21:08:01.934000", "terminatedAt": null, "status": "ACTIVE", "providerType": "hyperstack", "size": 500, "info": { "cloudId": null, "country": "US", "isMultinode": false, "dataCenterId": "US-1" }, "priceHr": 0.075, "stoppedPriceHr": 0.075, "provisioningPriceHr": 0.0, "userId": "clvb0itli0000oxevorvgrpfn", "teamId": null, "walletId": "clvb0iv0c0004oxev5mnt1xiv", "pods": [], "clusters": [] } ``` The response includes: * **`pods`**: Array of pod IDs currently using this disk * **`clusters`**: Array of cluster IDs currently using this disk * **`info`**: Additional metadata about disk location and capabilities ## Updating a Disk Currently, you can update the disk name: ```bash theme={null} curl --request PATCH \ --url https://api.primeintellect.ai/api/v1/disks/fdb5205fd9b14c9d804d3f70b4c96da0 \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "name": "updated-training-dataset" }' ``` ### Response ```json theme={null} { "id": "fdb5205fd9b14c9d804d3f70b4c96da0", "name": "updated-training-dataset" } ``` ## Deleting a Disk Terminate a disk when you no longer need it. This will permanently delete the disk and all its data. ```bash theme={null} curl --request DELETE \ --url https://api.primeintellect.ai/api/v1/disks/fdb5205fd9b14c9d804d3f70b4c96da0 \ --header 'Authorization: Bearer your_api_key' ``` ### Response ```json theme={null} { "status": "TERMINATED" } ``` > [!WARNING] > > **Disk deletion is permanent and irreversible**. All data stored on the disk will be lost. Make sure to backup any important data before deleting a disk. > [!NOTE] > > **Detachment Timing**: The disk detachment process can take longer than instance termination. After terminating an instance, you may need to wait a few moments before the disk becomes available for deletion or reuse. ## Disk Status Values Disks go through different statuses during their lifecycle: | Status | Description | | - | - | | `PROVISIONING` | Disk is being created | | `PENDING` | Disk status is changing | | `ACTIVE` | Disk is ready and can be attached to instances or deleted | | `STOPPED` | Disk is stopped | | `ERROR` | An error occurred during disk operations | | `DELETING` | Disk is being deleted | | `TERMINATED` | Disk has been deleted | | `UNKNOWN` | Disk status is unknown (cannot access the current status) | > [!NOTE] > > After terminating an instance, the disk detachment process may take a few moments to complete. You may need to wait briefly before the disk becomes available for deletion or reattachment. ## Common Use Cases ### Shared Training Data Create large disks with your training datasets that can be reused across multiple training runs. This is ideal for storing preprocessed data, large image datasets, or any training data that needs to be accessed by multiple GPU instances. By keeping your data on a persistent disk, you avoid having to re-upload or regenerate datasets for each new training session. ### Model Checkpoints and Artifacts Use disks to persist model checkpoints, trained weights, and training artifacts. This is especially valuable when running spot instances or interruptible workloads, as your progress remains safe even if the instance is terminated. You can resume training from the last checkpoint by simply attaching the disk to a new instance. ### Multi-Node Distributed Training For distributed training across multiple GPUs or nodes, use multinode-compatible disks that can be accessed simultaneously by multiple instances. This enables shared access to training data and synchronized checkpointing across your distributed training cluster. Check the `isMultinode` field in the disk availability response to verify support. ## Best Practices ### Location Planning * Create disks in the same datacenter where you plan to run GPU instances * Use the [availability endpoint](./check-gpu-availability) with `disks` filter to find compatible GPUs ### Size Planning * Start with the size needed - you cannot resize disks after creation * Consider future growth when selecting disk size * Remember that larger disks cost more per hour ### Data Management * Regularly backup important data from disks * Use descriptive names to identify disk contents ## Related Documentation * [Check GPU Availability](./check-gpu-availability) - Filter GPUs by disk location * [Provision GPU](./provision-gpu) - Attach disks when creating instances * [Managing Instances](./managing-pods) - Manage instances with attached disks # Managing Pods Source: https://docs.primeintellect.ai/api-reference/managing-pods How to get pods, statuses and delete instances > [!NOTE] > > Before you start, ensure that you have [API Key](./api-keys) with `Instances -> Read` permission (or `Read and > Write` if you want to `Delete` instances) ## Retrieving Existing Pods To view a list of active instances, use the [Get Pods](./pods/get-pods) endpoint. ```bash theme={null} curl --request GET \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' ``` This is a paginated request, with a default `limit` of **100** results. To adjust the number of results per page, use query parameters: ```bash theme={null} curl --request GET \ --url https://api.primeintellect.ai/api/v1/pods/?offset=0&limit=10 \ --header 'Authorization: Bearer your_api_key' ``` This will return a JSON object containing the total number of instances (`total_count`), the specified `limit` and `offset` and the list of pods under `data` (check out [Pod schema](./pods/get-pod) for details) ```json theme={null} { "total_count": 15, "offset": 0, "limit": 10, "data": [...] } ``` ### Retrieving a Specific Pod If you know the `podId`, you can retrieve details for that specific pod: ```bash theme={null} curl --request GET \ --url https://api.primeintellect.ai/api/v1/pods/my_pod_id \ --header 'Authorization: Bearer your_api_key' ``` ## Checking Pod Status After provisioning an instance, retrieve its status to obtain port mappings and connection details using the [Get Pod Status](./pods/status) endpoint. This request also supports checking multiple instance statuses simultaneously: ```bash theme={null} curl --request GET \ --url https://api.primeintellect.ai/api/v1/pods/status/?pod_ids=my_first_pod_id&pod_ids=my_second_pod_id \ --header 'Authorization: Bearer your_api_key' ``` It returns a `data` with the list of object like: ```json theme={null} { "podId": "my_first_pod_id", "providerType": "primeconpute", "status": "ACTIVE", "sshConnection": "root@135.23.125.123 -p 22", "costPerHr": 3.52, "primePortMapping": [ { "internal": "22", "external": "22", "protocol": "TCP", "usedBy": "SSH", "description": "" } ], "ip": "135.23.125.123", "installationFailure": null, "installationProgress": 100 } ``` If the instance has additional open ports, these will be listed under `primePortMapping`. > [!NOTE] > > In case of an error, `installationFailure` will be set. Our system will attempt automatic retries, but issues related to the image or configuration may require further action. ## Deleting a Pod To delete an instance you no longer need, use the [Delete Pod](./pods/delete-pod) endpoint. ```bash theme={null} curl --request DELETE \ --url https://api.primeintellect.ai/api/v1/pods/my_pod_id \ --header 'Authorization: Bearer your_api_key' ``` Once a pod is terminated, it is moved to history and becomes inaccessible via the standard [Get Pod](./pods/get-pod) or [Get Pods](./pods/get-pods) endpoints. To view historical data, use the [Get Pods History](./pods/get-pods-history) endpoint. # Create Pod Source: https://docs.primeintellect.ai/api-reference/pods/create-pod https://api.primeintellect.ai/openapi.json post /api/v1/pods/ # Delete Pod Source: https://docs.primeintellect.ai/api-reference/pods/delete-pod https://api.primeintellect.ai/openapi.json delete /api/v1/pods/{pod_id} # Get Pod Source: https://docs.primeintellect.ai/api-reference/pods/get-pod https://api.primeintellect.ai/openapi.json get /api/v1/pods/{pod_id} # Get Pod Logs Api Source: https://docs.primeintellect.ai/api-reference/pods/get-pod-logs-api https://api.primeintellect.ai/openapi.json get /api/v1/pods/{pod_id}/log Get logs for a pod. Args: tail: Number of most recent logs to return (default: 100) # Get Pods Source: https://docs.primeintellect.ai/api-reference/pods/get-pods https://api.primeintellect.ai/openapi.json get /api/v1/pods/ # Get Pods History Source: https://docs.primeintellect.ai/api-reference/pods/get-pods-history https://api.primeintellect.ai/openapi.json get /api/v1/pods/history # Get Pods Status Source: https://docs.primeintellect.ai/api-reference/pods/get-pods-status https://api.primeintellect.ai/openapi.json get /api/v1/pods/status # Provision Instance Source: https://docs.primeintellect.ai/api-reference/provision-gpu How to provision an instance using availability data > [!NOTE] > > Before you start, ensure that you have [API Key](./api-keys) with `Instances -> Read and write` permission ## Retrieving offers from the availability API > [!NOTE] > > For an in-depth guide to using the availability endpoint, refer to [This Guide](./check-gpu-availability) Our goal is to provision H100 GPU using availability data. First, we need to call the availability endpoint to get the current offers: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/gpus?gpu_type=H100_80GB®ions=united_states®ions=canada&gpu_count=1' \ --header 'Authorization: Bearer your_api_key' ``` We will use the Hyperstack provider and the following offer: ```json theme={null} { "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "provider": "hyperstack", "dataCenter": "CANADA-1", "country": "CA", "gpuCount": 1, "gpuMemory": 80, "disk": { "minCount": null, "defaultCount": 100, "maxCount": null, "pricePerUnit": null, "step": null, "defaultIncludedInPrice": null, "additionalInfo": null }, "vcpu": { "minCount": null, "defaultCount": 180, "maxCount": null, "pricePerUnit": null, "step": null, "defaultIncludedInPrice": null, "additionalInfo": null }, "memory": { "minCount": null, "defaultCount": 180, "maxCount": null, "pricePerUnit": null, "step": null, "defaultIncludedInPrice": null, "additionalInfo": null }, "internetSpeed": null, "interconnect": null, "interconnectType": null, "provisioningTime": null, "stockStatus": "Available", "security": "secure_cloud", "prices": { "onDemand": 1.9, "isVariable": null, "currency": "USD" }, "images": [ "ubuntu_22_cuda_12", "cuda_12_1_pytorch_2_2", "cuda_11_8_pytorch_2_1", "stable_diffusion", "flux", "axolotl", "bittensor", "vllm_llama_8b", "vllm_llama_70b", "vllm_llama_405b" ], "isSpot": null, "prepaidTime": null } ``` This GPU configuration has fixed resources, so we don't have to worry about those and just use the default ones. ## Creating the Instance Request Body First, lets go through the [**Create Pod**](./pods/create-pod) endpoint and explain how it works. The request requires a `body` with `pod`, `provider` and optional `team` definitions. ### `pod` The `pod` object defines the instance’s characteristics: ```json theme={null} "pod": { "name": "My first pod", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud" } ``` We can choose any `name`, but the rest of the parameters is copied from the availability offer. Since this offer includes a `dataCenterId` and `country`, we're going to pass those values during provisioning, as it indicates that the provider has GPUs with the same `cloudId` available in different locations. We also copy the rest of the GPU definition data: * `gpuType -> gpuType` * `socket -> socket` * `gpuCount -> gpuCount` * `security -> security` Last thing to do is to select an `image`. Available values are stored within the `images` property of the availability offer. We're going to select the default `ubuntu_22_cuda_12` image. ### `provider` ```json theme={null} "provider": { "type": "hyperstack" }, ``` The `provider` object is straightforward, we only need to specify the `type`, which in our case is `hyperstack`. ### `team` ```json theme={null} "team": { "teamId": "my_team_id" } ``` If you want to assign the pod to a specific `team`, include the team object. > [!TIP] > > You can find the `teamId` on your [Team's Profile page](https://app.primeintellect.ai/dashboard/team-profile) ### `sharedWithTeam` ```json theme={null} "sharedWithTeam": true ``` Set to `true` to share the instance with **all members** of the team. When enabled, every team member's SSH keys are added to the instance, giving them direct access. Requires `team.teamId` to be set. ### `teamMemberIds` ```json theme={null} "teamMemberIds": ["clvb1jtmk0001pxfv1abc2def", "clvb2kumr0002qygw3bcd4efg"] ``` Alternatively, provide a list of specific user IDs to share the instance with. Only those members' SSH keys will be added. Use the [List Team Members](./teams) endpoint to get user IDs. > [!NOTE] > > You can use either `sharedWithTeam` for all members or `teamMemberIds` for specific members. If both are provided, `sharedWithTeam` takes precedence. ## Sending the Create Request With all parts configured, the final request looks like this: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "My first pod", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud" }, "provider": { "type": "hyperstack" } }' ``` On successful completion, you should receive a `200 OK` response with the pod details in the [Response Body](./pods/create-pod). ## Modifying instance resources If the availability offer allows resource customization, you can adjust the default resources during provisioning. Below is an example of a modified resource configuration: ```json theme={null} "disk": { "minCount": 50, "defaultCount": 100, "maxCount": 1000, "pricePerUnit": 0.0003, "step": 10, "defaultIncludedInPrice": false, "additionalInfo": null }, "vcpu": { "minCount": 4, "defaultCount": 16, "maxCount": 32, "pricePerUnit": 0.004, "step": 2, "defaultIncludedInPrice": true, "additionalInfo": null }, ``` This configuration enables changes to `disk` and `vcpu` specifications. > [!NOTE] > > Be aware that default `vcpu` is included in price (`defaultIncludedInPrice` set to `true`) the default disk is not, which results in an **additional cost of \$0.03** when using the default disk size. ### Increase disk size To increase the disk size, set a new value in the `pod` property when sending the create request. For this example, since the provider supports increments of **10** with a minimum of **50**, we'll set the disk size to **200**. This adjustment will affect the total hourly cost as follows: ``` GPU cost: $2.69 vcpu cost: $0.00 (we're not paying for vcpu because `defaultIncludedInPrice == true`) memory cost: $0.00 disk cost: $0.06 (200 units * $0.0003) Total cost: $2.75 ``` So the final request will look like: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "My first pod", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud", "diskSize": 200 }, "provider": { "type": "hyperstack" } }' ``` ### Modifying vcpu This case is a little more complicated. Because `defaultIncludedInPrice` allows us to use default of **16 vcpus for free**, there are 2 options in which we're going to pay additional amount for vcpus #### Increasing vcpu Raising `vcpu` to **20** will increase the cost beyond the base instance price: ``` GPU cost: $2.69 vcpu cost: $0.08 (20 units * $0.004) memory cost: $0.00 disk cost: $0.03 (100 units[default] * $0.0003) Total cost: $2.80 ``` #### Decreasing vcpu Reducing `vcpu` to **10** can also increase costs compared to the default configuration. This is because some servers use predefined containers, and altering configurations may incur additional fees, making it more economical to use the default setup: ``` GPU cost: $2.69 vcpu cost: $0.04 (10 units * $0.004) memory cost: $0.00 disk cost: $0.03 (100 units[default] * $0.0003) Total cost: $2.76 ``` ### Example request with adjusted disk and vcpu With both `disk` and `vcpu` increased our request will look like: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "My first pod", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud", "diskSize": 200, "vcpus": 20 }, "provider": { "type": "hyperstack" } }' ``` and the total cost breakdown is as follows: ``` GPU cost: $2.69 vcpu cost: $0.08 (20 units * $0.004) memory cost: $0.00 disk cost: $0.06 (200 units[default] * $0.0003) Total cost: $2.83 ``` ## Dynamic pricing Certain offers feature dynamic pricing, meaning that rates may fluctuate throughout the instance’s lifetime due to factors such as: * Price being pegged to a foreign currency and fluctuating with exchange rates * Payment in alternative currencies or tokens * Market-based pricing If an offer supports dynamic pricing, `prices -> isVariable` will be set to `true`. In this case, it’s recommended to specify a `maxPrice` when provisioning the instance to set a cap: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "My first pod", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud", "maxPrice": 2.70, }, "provider": { "type": "hyperstack" } }' ``` This configuration limits provisioning to instances at or below the specified price. However, **prices may still vary during the instance’s lifetime**. ## Attaching Existing Disks You can attach existing disks with persisted data when provisioning a new GPU instance. This is particularly useful when you want to reuse datasets, models, or checkpoints stored on network-attached disks without re-uploading them. ### Finding Compatible GPUs Before provisioning, use the [availability endpoint](./check-gpu-availability) with the `disks` filter to find GPU instances that can attach to your existing disks: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/availability/gpus?disks=clhxy6aw80000j8080gdf8kqv&gpu_type=H100_80GB' \ --header 'Authorization: Bearer your_api_key' ``` This returns only GPU offers available in the same location as your disk. ### Provisioning with Disks To attach disks when creating an instance, add the `disks` array to your request body: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "Training instance with data", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "security": "secure_cloud" }, "provider": { "type": "hyperstack" }, "disks": ["clhxy6aw80000j8080gdf8kqv"] }' ``` ### Response On successful provisioning, you'll receive a response with the instance details and attached disk information: ```json theme={null} { "id": "11de9260fb52451085f3936c20b0ffdb", "userId": "clvb0itli0000oxevorvgrpfn", "teamId": null, "walletId": "clvb0iv0c0004oxev5mnt1xiv", "name": "Training instance with data", "providerType": "hyperstack", "status": "PROVISIONING", "installationStatus": "PENDING", "installationFailure": null, "installationProgress": null, "createdAt": "2025-11-07T23:36:36.391000", "updatedAt": "2025-11-07T23:36:36.418000", "terminatedAt": null, "gpuName": "H100_80GB", "gpuCount": 1, "socket": "PCIe", "priceHr": 1.9, "stoppedPriceHr": null, "provisioningPriceHr": 0.0, "environmentType": "ubuntu_22_cuda_12", "customTemplateId": null, "clusterId": null, "primePortMapping": [ { "internal": "22", "external": "22", "protocol": "TCP", "usedBy": "SSH", "description": "SSH access" }, { "internal": "*", "external": "*", "protocol": "TCP", "usedBy": null, "description": "All other ports" } ], "sshConnection": null, "ip": null, "resources": { "memory": 180, "disk": 1250, "sharedDisk": null, "vcpus": 28 }, "attachedResources": [ { "resourceType": "DISK", "id": "clhxy6aw80000j8080gdf8kqv", "status": "UNATTACHED", "isDetachable": true, "mountPath": "/data", "resourcePath": "/dev/vdc", "size": 500, "isShared": false }, { "resourceType": "DISK", "id": "ephemeral", "status": "ATTACHED", "isDetachable": false, "mountPath": "/ephemeral", "resourcePath": "/dev/vdb", "size": 750, "isShared": false } ], "isSpot": null, "autoRestart": null } ``` The response includes: * **`attachedResources`**: Array showing both your persistent disk (`clhxy6aw80000j8080gdf8kqv`) and the ephemeral instance disk * **`resources.disk`**: Total disk space including both persistent and ephemeral storage (500GB + 750GB = 1250GB) * **Disk status**: `UNATTACHED` initially, will become `ATTACHED` once the instance is fully provisioned, and then `MOUNTED` when disk it mounted to the working directory. > [!NOTE] > > The persistent disk will be automatically attached and mounted at `/data` once the instance completes provisioning. The ephemeral disk is immediately available at `/ephemeral`. ### Multiple Disks You can attach multiple disks to a single instance: ```bash theme={null} curl --request POST \ --url https://api.primeintellect.ai/api/v1/pods/ \ --header 'Authorization: Bearer your_api_key' \ --header 'Content-Type: application/json' \ --data '{ "pod": { "name": "Multi-disk training", "cloudId": "n3-H100x1", "gpuType": "H100_80GB", "socket": "PCIe", "gpuCount": 1, "image": "ubuntu_22_cuda_12", "dataCenterId": "CANADA-1", "country": "CA", "security": "secure_cloud" }, "provider": { "type": "hyperstack" }, "disks": [ "clhxy6aw80000j8080gdf8kqv", "clhxy9bz50001j8080hdf9lrw" ] }' ``` ### Use Cases * **Training with Large Datasets**: Attach disks containing preprocessed training data * **Model Checkpointing**: Resume training from checkpoints stored on persistent disks * **Shared Data Access**: Use multi-node compatible disks across multiple instances * **Spot Instances**: Keeping your data when running spot instances > [!WARNING] > > * Only disks you own can be attached to your instances > * Disks must be in an `ACTIVE` state > * Some providers may have limitations on the number of disks that can be attached # Create Secret Source: https://docs.primeintellect.ai/api-reference/secrets/create-secret https://api.primeintellect.ai/openapi.json post /api/v1/secrets/ Create a new global secret. The secret will be encrypted and stored securely. If teamId is provided, the secret will be associated with that team (requires team membership). # Delete Secret Source: https://docs.primeintellect.ai/api-reference/secrets/delete-secret https://api.primeintellect.ai/openapi.json delete /api/v1/secrets/{secret_id} Delete a secret. This operation cannot be undone. Secrets that are linked to environments will be automatically unlinked. # Get Secret Source: https://docs.primeintellect.ai/api-reference/secrets/get-secret https://api.primeintellect.ai/openapi.json get /api/v1/secrets/{secret_id} Get a specific secret by ID. Note: The secret value is not returned, only metadata. # List Secrets Source: https://docs.primeintellect.ai/api-reference/secrets/list-secrets https://api.primeintellect.ai/openapi.json get /api/v1/secrets/ List global secrets for the authenticated user or a team. If teamId is provided, returns secrets for that team (requires team membership). Otherwise, returns the user's personal secrets. # Update Secret Source: https://docs.primeintellect.ai/api-reference/secrets/update-secret https://api.primeintellect.ai/openapi.json patch /api/v1/secrets/{secret_id} Update an existing secret. You can update the name, value, and/or description. # Add Member Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/add-member https://api.primeintellect.ai/openapi.json post /api/v1/slurm-clusters/{team_id}/{job_id}/members # Deploy Cluster Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/deploy-cluster https://api.primeintellect.ai/openapi.json post /api/v1/slurm-clusters/{team_id}/deploy Deploy a Slurm cluster onto one of the team's dedicated clusters. Team-admin gated like resize and add/remove member. The cluster must belong to `team_id` and have self-service enabled (checked by the service, which also pins the new cluster to this team). Node planning and claiming run before this returns, so "not enough nodes" or a port conflict is an immediate error; the Helm install continues in the background and the returned cluster starts out PENDING. # Get Connection Info Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/get-connection-info https://api.primeintellect.ai/openapi.json get /api/v1/slurm-clusters/{cluster_id}/connection SSH coordinates for one RUNNING Slurm cluster deployment. Team-member gated like the roster reads; ownership follows the same visibility boundary (`assert_job_visible_to_team` normalizes every cross-team/wrong-type/missing probe to a generic 404, so probing other teams' job ids distinguishes nothing). The payload carries only the connect flags and coordinates — no release names or namespaces. # List Clusters Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/list-clusters https://api.primeintellect.ai/openapi.json get /api/v1/slurm-clusters/{team_id} # List Members Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/list-members https://api.primeintellect.ai/openapi.json get /api/v1/slurm-clusters/{team_id}/{job_id}/members # Remove Member Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/remove-member https://api.primeintellect.ai/openapi.json delete /api/v1/slurm-clusters/{team_id}/{job_id}/members/{username} # Resize Cluster Source: https://docs.primeintellect.ai/api-reference/slurm-clusters/resize-cluster https://api.primeintellect.ai/openapi.json post /api/v1/slurm-clusters/{team_id}/{job_id}/resize Resize a Slurm cluster by changing its worker node count. Requires `factory:write` (see module docstring); `slurm_clusters` scopes alone are rejected. Team-admin gated like add/remove member. The cluster must have self-service enabled (checked by the resize service). Grows by claiming eligible idle nodes (same filters as deploy); shrinks by releasing only idle nodes, never touching running jobs. Returns the verified membership after the mutation settles — the actual final size may differ from the request if node release was best-effort and fell short. # Delete Ssh Key Source: https://docs.primeintellect.ai/api-reference/ssh-keys/delete-ssh-key https://api.primeintellect.ai/openapi.json delete /api/v1/ssh_keys/{key_id} # Get Ssh Keys Source: https://docs.primeintellect.ai/api-reference/ssh-keys/get-ssh-keys https://api.primeintellect.ai/openapi.json get /api/v1/ssh_keys/ # Set Primary Key Source: https://docs.primeintellect.ai/api-reference/ssh-keys/set-primary-key https://api.primeintellect.ai/openapi.json patch /api/v1/ssh_keys/{key_id} # Upload Ssh Key Source: https://docs.primeintellect.ai/api-reference/ssh-keys/upload-ssh-key https://api.primeintellect.ai/openapi.json post /api/v1/ssh_keys/ # Teams Source: https://docs.primeintellect.ai/api-reference/teams How to list teams and team members using the API > [!NOTE] > > Before you start, ensure that you have an [API Key](./api-keys) with `Teams -> Read` permission ## Listing Your Teams Retrieve the teams you belong to using the `GET /user/teams` endpoint. ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/user/teams' \ --header 'Authorization: Bearer your_api_key' ``` This is a paginated request with a default `limit` of **100** results. Use query parameters to adjust: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/user/teams?offset=0&limit=50' \ --header 'Authorization: Bearer your_api_key' ``` ### Response ```json theme={null} { "total_count": 2, "offset": 0, "limit": 50, "data": [ { "teamId": "clvb0itli0000oxevorvgrpfn", "name": "My Team", "slug": "my-team", "role": "ADMIN", "createdAt": "2025-01-15T10:30:00.000000" }, { "teamId": "clvb1jtmk0001pxfv1abc2def", "name": "Research Lab", "slug": "research-lab", "role": "MEMBER", "createdAt": "2025-02-01T14:20:00.000000" } ] } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `teamId` | string | Unique team identifier | | `name` | string | Team display name | | `slug` | string | Team URL slug | | `role` | string | Your role in the team (`ADMIN` or `MEMBER`) | | `createdAt` | string | ISO 8601 timestamp of when you joined the team | ## Listing Team Members Retrieve the members of a team using the `GET /teams/{team_id}/members` endpoint. You must be a member of the team to access this endpoint. ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/teams/{team_id}/members' \ --header 'Authorization: Bearer your_api_key' ``` This is a paginated request with a default `limit` of **100** results. Use query parameters to adjust: ```bash theme={null} curl --request GET \ --url 'https://api.primeintellect.ai/api/v1/teams/{team_id}/members?offset=0&limit=50' \ --header 'Authorization: Bearer your_api_key' ``` ### Response ```json theme={null} { "total_count": 2, "offset": 0, "limit": 50, "data": [ { "userId": "clvb0itli0000oxevorvgrpfn", "userName": "Alice", "userEmail": "alice@example.com", "role": "ADMIN", "joinedAt": "2025-01-15T10:30:00.000000" }, { "userId": "clvb1jtmk0001pxfv1abc2def", "userName": "Bob", "userEmail": "bob@example.com", "role": "MEMBER", "joinedAt": "2025-02-01T14:20:00.000000" } ] } ``` ### Response Fields | Field | Type | Description | | - | - | - | | `userId` | string | Unique user identifier | | `userName` | string | User's display name | | `userEmail` | string | User's email address | | `role` | string | Team role (`ADMIN` or `MEMBER`) | | `joinedAt` | string | ISO 8601 timestamp of when the user joined the team | > [!TIP] > > You can find your `teamId` on your [Team's Profile page](https://app.primeintellect.ai/dashboard/team-profile) or by calling `GET /user/teams`. # List Team Members Source: https://docs.primeintellect.ai/api-reference/teams/list-team-members https://api.primeintellect.ai/openapi.json get /api/v1/teams/{team_id}/members # Get an episode Source: https://docs.primeintellect.ai/api-reference/traces/episodes/get-an-episode https://prime-traces.pintel.dev/openapi.json get /api/v1/episodes/{episode_id} An episode's own fields plus totals computed from its traces: tokens, agent time, participating agents and whether any trace failed. `has_error` reports an error on the episode itself, and `any_trace_error` reports one on any member trace, so an episode-level failure stays visible even when every trace succeeded. An episode with no traces is valid and returns empty totals. With `raw=true`, returns the stored episode document, with `traces` listing the member trace IDs. # List episode traces Source: https://docs.primeintellect.ai/api-reference/traces/episodes/list-episode-traces https://prime-traces.pintel.dev/openapi.json get /api/v1/episodes/{episode_id}/traces Summaries of the traces in an episode, cursor-paginated. An episode that does not exist returns `404`, so an empty page always means an existing episode with no matching traces. # List episodes Source: https://docs.primeintellect.ai/api-reference/traces/episodes/list-episodes https://prime-traces.pintel.dev/openapi.json get /api/v1/episodes Episode summaries for the authenticated owner, newest first. Cursor-paginated like the trace list. `upload_id` is the ID in an upload receipt, so it returns the episodes one upload stored. `context.=` matches the context map supplied at upload. Episodes store no context of their own, so it is matched on member traces: an episode with no traces never matches. # Upload traces Source: https://docs.primeintellect.ai/api-reference/traces/ingestion/upload-traces https://prime-traces.pintel.dev/openapi.json post /api/v1/traces Upload traces as a `multipart/form-data` request with two parts: * `metadata` (`application/json`): `{"schema_version": 1, "context": {...}}`. `context` is an optional flat string map stored on every trace in the upload. * `traces` (`application/x-ndjson`): one complete trace per line, one complete episode per line with `X-Prime-Line-Format: episode`, or one Prime Agent session per line with `X-Prime-Line-Format: prime_agent` (a JSON array of the session file's lines, header first). The part may be sent with `Content-Encoding: gzip`. `Idempotency-Key` is `sha256:<64 lowercase hex>` of the uncompressed `traces` bytes, newlines included, and is also the upload ID. A request can be up to 256 MiB and a single line up to 64 MiB, both measured uncompressed. `201` means every line was stored; there is no partial success. `400` means the content was rejected and nothing was stored. `409` means the same bytes were already uploaded with a different line format. `429` and `503` are safe to retry with the same bytes after `Retry-After`. # Get a run Source: https://docs.primeintellect.ai/api-reference/traces/runs/get-a-run https://prime-traces.pintel.dev/openapi.json get /api/v1/runs/{run_id} One run's totals over its traces. A run with no traces returns `404`. # List run steps Source: https://docs.primeintellect.ai/api-reference/traces/runs/list-run-steps https://prime-traces.pintel.dev/openapi.json get /api/v1/runs/{run_id}/steps The distinct `run_step` values recorded in a run, with each step's trace count, episode count and average reward. # List runs Source: https://docs.primeintellect.ai/api-reference/traces/runs/list-runs https://prime-traces.pintel.dev/openapi.json get /api/v1/runs Runs for the authenticated owner, with totals over their traces: trace and episode counts, average reward, step range, and the environments and models used. Cursor-paginated. # Search a run Source: https://docs.primeintellect.ai/api-reference/traces/runs/search-a-run https://prime-traces.pintel.dev/openapi.json get /api/v1/runs/{run_id}/search Case-sensitive literal search over the content of a run's traces. The query needs at least three characters. Returns one match per node, with Unicode offsets and an excerpt. Choose what to search with `field` (`content`, `reasoning_content` or `tool_calls`) and narrow with the other filters. Continue with `next_cursor` and the same filters until it is null. Only indexed traces are searched. The first page's `coverage` reports traces that were not yet searchable. A search that reads more than the server allows returns `400` with `search_limit_exceeded`; retrying unchanged will not help, so make the query more specific or add filters. # Delete a run's traces Source: https://docs.primeintellect.ai/api-reference/traces/traces/delete-a-runs-traces https://prime-traces.pintel.dev/openapi.json delete /api/v1/traces Delete every trace in a run. `run_id` is required. Only traces are deleted; episodes in the run are kept. The delete covers the traces stored when it looks up the run: an upload that finishes while the request is in progress may or may not be deleted, and one that finishes after it returns is not affected. Returns `202` once the traces are no longer readable. A run with no traces returns `404`, including a repeat of a delete that already succeeded. # Delete a trace Source: https://docs.primeintellect.ai/api-reference/traces/traces/delete-a-trace https://prime-traces.pintel.dev/openapi.json delete /api/v1/traces/{trace_id} Delete every stored copy of a trace. Returns `202` once the trace is no longer readable; storage is reclaimed in the background. `created_at` is an optional hint used only to check that the trace exists: a hint that matches no stored copy returns `404`. Deleting a trace that is already gone also returns `404`. # Get a trace Source: https://docs.primeintellect.ai/api-reference/traces/traces/get-a-trace https://prime-traces.pintel.dev/openapi.json get /api/v1/traces/{trace_id} A trace's summary, or with `raw=true` the stored trace document as uploaded, with recognized credentials replaced by ``. The raw document can be large, up to the 64 MiB line limit, so fetch the summary first when you only need its fields. `size_bytes` on the summary tells you how large the raw document is. # List trace model calls Source: https://docs.primeintellect.ai/api-reference/traces/traces/list-trace-model-calls https://prime-traces.pintel.dev/openapi.json get /api/v1/traces/{trace_id}/calls A trace's model calls in order, for timelines: spans, models and finish reasons. Indexing, `409 trace_not_indexed`, cursor rules and `partial_index` work as for trace nodes. A call's start and end times are omitted if either is missing or the end precedes the start. `node_idx` is null when the call recorded no valid node reference. # List trace nodes Source: https://docs.primeintellect.ai/api-reference/traces/traces/list-trace-nodes https://prime-traces.pintel.dev/openapi.json get /api/v1/traces/{trace_id}/nodes A trace's message nodes in step order, without downloading the whole trace. Use this to render a trace page by page. Nodes come from an index built shortly after upload. Until it is ready the request returns `409` with `trace_not_indexed`; retry, or read the trace with `raw=true`. For very large traces the index stops at a per-trace cap, and `partial_index: true` on the page means the rest is only available with `raw=true`. If the trace is re-uploaded while you are paging, the cursor is rejected rather than mixing the two versions. # List traces Source: https://docs.primeintellect.ai/api-reference/traces/traces/list-traces https://prime-traces.pintel.dev/openapi.json get /api/v1/traces Trace summaries for the authenticated owner, newest first by default. Narrow the list with the query parameters below; an unknown parameter is rejected with `invalid_filter` rather than ignored. `created_after` and `created_before` are the cheapest filters. `sort` is one of `created_at`, `reward`, `duration_ms` or `run_step`. `upload_id` is the ID in an upload receipt, so it returns the traces one upload stored. If a trace was uploaded more than once, the newest copy is returned. Copies with different `created_at` values can still appear on two different pages. Pages are cursor-paginated: pass `next_cursor` back as `cursor` with the same filters. # Create Dedicated Run Source: https://docs.primeintellect.ai/api-reference/training/create-dedicated-run https://api.primeintellect.ai/openapi.json post /api/v1/training/runs Dispatch a dedicated full-FT prime-rl run on a registered PrimeCluster. Access is gated by ClusterAllocation - the picker in `training_service.create_dedicated_run` rejects callers whose team/user has no matching allocation on a live PrimeCluster. When `team_id` is set the caller must additionally be a member of that team (mirrors the LoRA/shared dispatch path). Admission checks (runtime pins, hub env ids, HF repo names, per-run GPU cap) run sync so bad configs return 400 immediately. Local clients supply normalized configs; legacy clients use the validator schema. Cluster-side probes (GPU capacity, model cache) and the helm install run async in a Cloud Task; callers poll `GET /api/v1/rft/runs/{run_id}` for status transitions PENDING -> CREATING -> RUNNING (or FAILED with errorMessage on a cluster-side rejection). # Create Volume Source: https://docs.primeintellect.ai/api-reference/training/create-volume https://api.primeintellect.ai/openapi.json post /api/v1/training/volumes # Create Volume Session Source: https://docs.primeintellect.ai/api-reference/training/create-volume-session https://api.primeintellect.ai/openapi.json post /api/v1/training/volumes/{name}/sessions # Delete Dedicated Run Source: https://docs.primeintellect.ai/api-reference/training/delete-dedicated-run https://api.primeintellect.ai/openapi.json delete /api/v1/training/runs/{run_id} Delete a dedicated run: helm uninstall + namespace delete + DB cleanup. Idempotent. Safe to call repeatedly. Cleans up the linked Job row too (which uninstalls the helm release on the customer cluster). Soft- deletes the RFTRun row (matches the existing run-delete UX - Billing refs are preserved). Access is per-row: `verify_run_ownership` accepts personal runs the caller owns and team runs the caller is a member of. # Delete Volume Source: https://docs.primeintellect.ai/api-reference/training/delete-volume https://api.primeintellect.ai/openapi.json delete /api/v1/training/volumes/{name} # Expand Volume Source: https://docs.primeintellect.ai/api-reference/training/expand-volume https://api.primeintellect.ai/openapi.json patch /api/v1/training/volumes/{name} Raise a volume's size cap; volumes can't shrink. # Get Available Fft Models Source: https://docs.primeintellect.ai/api-reference/training/get-available-fft-models https://api.primeintellect.ai/openapi.json get /api/v1/training/available-fft-models Models pre-cached on the caller's eligible PrimeClusters for FFT dispatch. Same principal collapse as the dispatch picker (team wins when present, otherwise personal) and the same cluster filter (uncordoned + heartbeat fresh + non-zero ClusterAllocation) so what surfaces here is what dispatch would actually land on. Only PRESENT-cache clusters contribute — ABSENT / ERROR would 400 the dispatch and NULL (unprobed) hasn't reported a manifest yet. Each model carries the list of clusters it's warm on with `gpu_type` + `cache_synced_at` so callers can pin the follow-up dispatch to a specific GPU family without hitting the "picker landed on a cluster that has the GPU but not this model" reject path. # Get Available Gpu Types Source: https://docs.primeintellect.ai/api-reference/training/get-available-gpu-types https://api.primeintellect.ai/openapi.json get /api/v1/training/available-gpu-types Distinct GPU types the caller could dispatch a dedicated FFT run on. Same principal collapse as the dispatch picker (team wins when present, otherwise personal) so what the dropdown offers matches what dispatch would land on. When `team_id` is set the caller must be a team member, matching the create-run gate. # Get Volume Session Source: https://docs.primeintellect.ai/api-reference/training/get-volume-session https://api.primeintellect.ai/openapi.json get /api/v1/training/volumes/{name}/sessions/{session_id} # List Volume Sessions Source: https://docs.primeintellect.ai/api-reference/training/list-volume-sessions https://api.primeintellect.ai/openapi.json get /api/v1/training/volumes/{name}/sessions The caller's sessions that still block deleting the volume. # List Volumes Source: https://docs.primeintellect.ai/api-reference/training/list-volumes https://api.primeintellect.ai/openapi.json get /api/v1/training/volumes # Pause Dedicated Run Source: https://docs.primeintellect.ai/api-reference/training/pause-dedicated-run https://api.primeintellect.ai/openapi.json post /api/v1/training/runs/{run_id}/pause Pause a running dedicated run for good at its next checkpoint. The run turns PAUSING, then PAUSED once its checkpoint is back on its volume in R2 and its GPUs are released. Resume it with a new run from that volume: `resumeFrom` on POST /runs. # Prepare Local Validation Source: https://docs.primeintellect.ai/api-reference/training/prepare-local-validation https://api.primeintellect.ai/openapi.json post /api/v1/training/runs/prepare Resolve the default/selected runtime for local CLI config validation. No run, run token, GPU allocation, volume or deployment is created. The result is metadata, not authorization to submit a compiled artifact. # Resume Dedicated Run Source: https://docs.primeintellect.ai/api-reference/training/resume-dedicated-run https://api.primeintellect.ai/openapi.json post /api/v1/training/runs/{run_id}/resume Resume a PAUSED dedicated run as a new run from its checkpoint. The new run reuses the paused run's config, team, name, mode, image and GPU type, attaches its volume and starts from its last checkpoint. `gpuType` and `imageTag` override the stored ones; secrets are sent again because they are dropped once a run deploys. # Stop Volume Session Source: https://docs.primeintellect.ai/api-reference/training/stop-volume-session https://api.primeintellect.ai/openapi.json delete /api/v1/training/volumes/{name}/sessions/{session_id} # Volume Transfer Source: https://docs.primeintellect.ai/api-reference/training/volume-transfer https://api.primeintellect.ai/openapi.json post /api/v1/training/volumes/{name}/transfer Where `prime volumes get/put` should transfer (ENG-6585). A put needs rft:write, like every other volume write. # Volume Transfer Release Source: https://docs.primeintellect.ai/api-reference/training/volume-transfer-release https://api.primeintellect.ai/openapi.json post /api/v1/training/volumes/{name}/transfer/release A finished direct put releases its lease (`leaseId` from transfer), so runs and deletes stop waiting on it. Idempotent; needs what a put needs (rft:write). # Bulk Delete Tunnels Endpoint Source: https://docs.primeintellect.ai/api-reference/tunnel/bulk-delete-tunnels-endpoint https://api.primeintellect.ai/openapi.json delete /api/v1/tunnel Bulk soft-delete multiple tunnels by IDs or labels. ## Rate Limit 100 requests per 60 seconds per user. # Create Tunnel Endpoint Source: https://docs.primeintellect.ai/api-reference/tunnel/create-tunnel-endpoint https://api.primeintellect.ai/openapi.json post /api/v1/tunnel Create a new tunnel for exposing a local service. ## Rate Limit 600 requests per 60 seconds per user. # Delete Tunnel Endpoint Source: https://docs.primeintellect.ai/api-reference/tunnel/delete-tunnel-endpoint https://api.primeintellect.ai/openapi.json delete /api/v1/tunnel/{tunnel_id} Delete a tunnel and disconnect any active clients. ## Rate Limit 100 requests per 60 seconds per user. # Get Tunnel Status Endpoint Source: https://docs.primeintellect.ai/api-reference/tunnel/get-tunnel-status-endpoint https://api.primeintellect.ai/openapi.json get /api/v1/tunnel/{tunnel_id} Get status of a specific tunnel. ## Rate Limit 300 requests per 60 seconds per tunnel. # List Tunnels Endpoint Source: https://docs.primeintellect.ai/api-reference/tunnel/list-tunnels-endpoint https://api.primeintellect.ai/openapi.json get /api/v1/tunnel List all active tunnels for the current user. ## Rate Limit 300 requests per 60 seconds per user. # Get Whoami Source: https://docs.primeintellect.ai/api-reference/user/get-whoami https://api.primeintellect.ai/openapi.json get /api/v1/user/whoami # List My Teams Source: https://docs.primeintellect.ai/api-reference/user/list-my-teams https://api.primeintellect.ai/openapi.json get /api/v1/user/teams # Set Username Slug Source: https://docs.primeintellect.ai/api-reference/user/set-username-slug https://api.primeintellect.ai/openapi.json patch /api/v1/user/slug # Get Availability Information Source: https://docs.primeintellect.ai/cli-reference/check-gpu-availability How to check GPU and disk availability and pricing > [!NOTE] > > Before you start, ensure you have set your API keys and SSH key path as outlined in the [CLI introduction](/cli-reference/introduction). ## Retrieving GPU Availability Data To see all available GPU types and their current pricing across all regions, simply run: ```bash theme={null} prime availability list ``` This command will display a table showing all available GPU configurations, including details like GPU type, count, location, price per hour, and stock status. ## Retrieving Disk Availability Data To see all available disk storage options across providers and datacenters: ```bash theme={null} prime availability disks ``` This displays available persistent storage configurations with pricing, maximum capacity, and multinode support information. ## Retrieving Filtered GPU Availability Data You can then use filters like `--gpu-type`, `--regions`, or `--gpu-count` to narrow down the results. Here are all available filters: ### GPU Availability Filters * `--gpu-type`: Filter by specific GPU model * Example: `--gpu-type H100_80GB` * Optional string value * `--gpu-count`: Filter by number of GPUs * Example: `--gpu-count 2` * Optional integer value * `--regions`: Filter by geographic regions * Example: `--regions united_states,canada` or `--regions united_states --regions canada` * Optional, can specify multiple regions * `--socket`: Filter by socket type * Example: `--socket PCIe` * Accepts values: `PCIe`, `SXM2`, `SXM3`, `SXM4`, `SXM5` * Optional string value * `--disks`: Filter GPUs by disk IDs (shows GPUs in same location as disks) * Example: `--disks disk-id-1 --disks disk-id-2` * Optional, can specify multiple disk IDs * Useful for finding GPUs compatible with your existing disks * `--group-similar`: Group similar configurations from same provider * Example: `--no-group-similar` * Default: true * Optional boolean flag ### Example with Multiple Filters ```bash theme={null} prime availability list --gpu-type H100_80GB --regions united_states --socket PCIe --no-group-similar ``` ### Finding GPUs Compatible with Your Disks If you have existing disks and want to find GPUs in the same datacenter/region: ```bash theme={null} # First, list your disks to get their IDs prime disks list # Then filter GPUs by disk IDs prime availability list --disks disk-id-1 --disks disk-id-2 ``` This ensures you see only GPU configurations that can access your existing disks. ## Retrieving Filtered Disk Availability Data ### Disk Availability Filters * `--regions`: Filter by geographic regions * Example: `--regions united_states,eu_north` * Optional, can specify multiple regions * `--data-center-id`: Filter by specific datacenter * Example: `--data-center-id US-1` * Optional string value * `--cloud-id`: Filter by cloud configuration ID * Example: `--cloud-id "Provider Storage A"` * Optional string value ### Example with Filters ```bash theme={null} prime availability disks --regions united_states --data-center-id US-1 ``` ## Understanding the Terminal Output ### GPU Availability Output Here's an example of what the command output looks like: | ID | GPU Type | GPUs | Socket | Provider | Location | Stock | Price/Hr | Memory (GB) | Security | vCPUs | RAM (GB) | | - | - | - | - | - | - | - | - | - | - | - | - | | 346663 | H100\_80GB | 2 | PCIe | runpod | CA | Low | \$5.40 | 160 | secure\_cloud | 32 | 502 | | 551ffd | H100\_80GB | 2 | PCIe | runpod | US | Low | \$5.40 | 160 | secure\_cloud | 32 | 502 | Each row represents a unique GPU configuration available for deployment. The output includes: * A unique ID for the configuration * GPU specifications (type, count, socket) * Provider and location information * Current stock status * Pricing and hardware details ### Disk Availability Output For disk availability, the output includes: | ID | Provider | Location | Stock | Price/Hr/GB | Max Size (GB) | Is Multinode | | - | - | - | - | - | - | - | | 7d2232 | runpod | US (US-WA-1) | Available | \$0.00011111 | 8192 | Yes | | 472b26 | hyperstack | NO (NORWAY-1) | Available | \$0.00009700 | 100000 | No | Each row represents available disk storage with: * A unique ID for the configuration * Provider offering the storage * Location (country and datacenter) * Stock availability status * Price per GB per hour * Maximum disk size supported * Whether the disk supports multinode (can be attached to multiple instances) # Configuration Source: https://docs.primeintellect.ai/cli-reference/config-cli Configure your Prime CLI settings The Prime CLI provides several commands to manage your configuration settings. All configuration commands are accessed through the `prime config` command group. ## View Configuration View your current configuration settings: ```bash theme={null} prime config view ``` This displays a table with your current settings, including: * API Key (partially masked) * Team ID * Base URL * SSH Key Path ## API Key Management Set your Prime Intellect API key: ```bash theme={null} prime config set-api-key ``` ## Team Settings > **Set Team ID** > > Configure your team ID for team-based access: > > ```bash theme={null} > prime config set-team-id > ``` > **Remove Team ID** > > Switch back to your personal account: > > ```bash theme={null} > prime config remove-team-id > ``` ## Sharing Settings Configure whether new instances are automatically shared with all team members: ```bash theme={null} prime config set-share-resources-with-team true ``` When enabled, `prime pods create` will automatically set `sharedWithTeam` on new instances without needing to pass `--share-with-team` each time. Set to `false` to disable: ```bash theme={null} prime config set-share-resources-with-team false ``` ## Prime Traces uploads CLI v0.9.4 documents a machine-level opt-out for runs using the Prime Runs SDK: ```bash theme={null} prime config set-traces-opt-out true ``` Set it to `false` to allow uploads again, subject to workspace settings and higher-precedence client overrides. Opted-out runs use legacy sample storage only while that service remains available. See [upload controls and precedence](/traces/pricing-and-settings#opt-out-for-a-run-or-client) and the [November 6 legacy retirement](/traces/legacy-storage). ## Connection Settings > **Base URL** > > Set the API endpoint URL: > > ```bash theme={null} > prime config set-base-url > ``` > **SSH Key** > > Configure SSH key path: > > ```bash theme={null} > prime config set-ssh-key-path > ``` ## Reset Configuration Reset all settings to their default values: ```bash theme={null} prime config reset ``` > [!WARNING] > > This will remove all your custom settings, including your API key. You'll need to reconfigure the CLI after resetting. ## Configuration Options | Command | Description | Default | | - | - | - | | `view` | Display current configuration | - | | `set-api-key` | Set your API key | - | | `set-team-id` | Set team ID for team access | - | | `remove-team-id` | Switch to personal account | - | | `set-base-url` | Set API base URL | `https://api.primeintellect.ai` | | `set-ssh-key-path` | Set SSH private key path | `~/.ssh/id_rsa` | | `set-share-resources-with-team` | Auto-share new instances with team | `false` | | `reset` | Reset to default settings | - | > [!TIP] > > All configuration commands will prompt for values if not provided as command-line arguments. # Environments Hub Source: https://docs.primeintellect.ai/cli-reference/environments Create, install and manage environments from the Environments Hub > [!NOTE] > > For documentation on how to use the Prime CLI with the Environments Hub, refer to the [Environments Hub Tutorial](/tutorials-environments/getting-started). # Overview Source: https://docs.primeintellect.ai/cli-reference/introduction Command line interface for managing Prime Intellect compute, RL environments and code sandboxes. The Prime Intellect CLI provides a powerful command-line interface for managing all of our Prime Intellect offerings. Including: * Managing compute resources on our platform * Creating, publishing and installing RL environments from our [environment hub](https://app.primeintellect.ai/dashboard/environments) * Managing code sandboxes for secure code execution > [!NOTE] > > Check out our [open source repository](https://github.com/PrimeIntellect-ai/prime) on GitHub. ## Quick Start 1. Install uv (if not already installed): ```bash theme={null} curl -LsSf https://astral.sh/uv/install.sh | sh ``` 2. Install the CLI with uv: ```bash theme={null} uv tool install prime ``` 3. Authenticate: ```bash theme={null} prime login ``` Alternatively you can also manually set your [API key](/api-reference/api-keys) using `prime config set-api-key` 4. Configure SSH key for pod access: ```bash theme={null} prime config set-ssh-key-path ``` \[!TIP] You can generate your ssh keys on [app.primeintellect.ai](https://app.primeintellect.ai/dashboard/profile) under the /dashboard/profile section. We recommend creating a dedicated directory for your Prime Intellect keys in your home directory, like `/Users//pi-keys`. 5\. Verify your configuration: ```bash theme={null} prime config view ``` ![View CLI Configuration](https://mintcdn.com/primeintellect/u4gd9w_CirhPniY2/images/cli/cli-config-view.png?fit=max\&auto=format\&n=u4gd9w_CirhPniY2\&q=85\&s=565c51879381e689fb53a1f2b807c6c4) ### Alternative Installation: Using pip If you prefer not to use uv: ```bash theme={null} pip install prime ``` ## Key Features * **GPU Resource Management**: Query and filter available GPU resources * **Pod Management**: Create, monitor, and terminate compute pods * **SSH Access**: Direct SSH access to running pods * **Team Support**: Manage resources across team environments ## Basic Commands ```bash theme={null} # List available GPUs prime availability list # Filter by GPU type prime availability list --gpu-type H100_80GB # Create a pod prime pods create # List your pods prime pods list # SSH into a pod prime pods ssh ``` For detailed documentation on each command and feature, explore the sections above. > **[Check GPU availability](/cli-reference/check-gpu-availability)** > > View and filter available GPU resources across providers using simple commands. > **[Provision Instances](/cli-reference/provision-gpu)** > > Create, monitor, and manage compute pods > directly from your terminal. > **[Config Settings](/cli-reference/config-cli)** > > Configure connection settings and team accounts. # Managing Disks Source: https://docs.primeintellect.ai/cli-reference/managing-disks How to create and manage network-attached storage using the CLI > [!NOTE] > > Before you start, ensure that you have [API Key](/api-reference/api-keys) with `Disks -> Read and write` permission ## Overview Network-attached disks provide persistent storage that can be shared across multiple GPU instances. Disks persist independently from instances, making them ideal for storing datasets, model checkpoints, and other data that needs to survive instance termination. ## Checking Disk Availability Before creating a disk, check available storage options: ```bash theme={null} prime availability disks ``` > [!TIP] > > Filter results using these options: > > * `--regions`: Geographic locations (e.g., united\_states,eu\_north) > * `--data-center-id`: Specific datacenter (e.g., US-1) > * `--cloud-id`: Cloud configuration ID Example with filters: ```bash theme={null} prime availability disks --regions united_states ``` ### Example Output | ID | Provider | Location | Stock | Price/Hr/GB | Max Size (GB) | Is Multinode | | - | - | - | - | - | - | - | | c008ad | runpod | US (US-CA-2) | Available | \$0.00011111 | 8,192 | Yes | | 4e50ab | runpod | US (US-IL-1) | Available | \$0.00011111 | 8,192 | Yes | | 4eb3b5 | runpod | US (US-KS-2) | Available | \$0.00011111 | 8,192 | Yes | | 334eaa | runpod | US (US-MO-1) | Available | \$0.00011111 | 8,192 | Yes | | d9c8e5 | runpod | US (US-NC-1) | Available | \$0.00011111 | 8,192 | Yes | | 241b41 | runpod | US (US-TX-3) | Available | \$0.00011111 | 8,192 | Yes | | 7d2232 | runpod | US (US-WA-1) | Available | \$0.00011111 | 8,192 | Yes | | a869c3 | hyperstack | US (US-1) | Available | \$0.00009700 | 100,000 | No | | 8121df | crusoecloud | US (us-southcentral1-a) | Available | \$0.00011546 | 8,192 | No | | 81cdd2 | crusoecloud | US (us-east1-a) | Available | \$0.00011546 | 8,192 | No | | 2f139f | dc\_roan | US | Available | \$0.00007000 | 8,192 | Yes | **Column Descriptions:** * **ID**: Short identifier for easy disk creation (use with `--id` parameter) * **Provider**: Storage provider offering the disk (runpod, hyperstack, crusoecloud, dc\_roan) * **Location**: Country code and datacenter identifier * **Stock**: Current availability status * **Price/Hr/GB**: Cost per gigabyte per hour (hourly rate × disk size = total hourly cost) * **Max Size (GB)**: Maximum disk capacity supported by the provider * **Is Multinode**: Whether the disk can be attached to multiple instances simultaneously ## Creating a Disk Create a new persistent disk: ```bash theme={null} prime disks create --id c008ad --size 500 ``` ### Available Options **--id** `string` *required* Short ID from availability disks list **--size** `integer` *required* Disk size in GB (must be within provider's min/max limits) **--name** `string` Human-friendly name for the disk (auto-generated if not provided) **--team-id** `string` Team identifier for the disk **--yes** `boolean` Skip confirmation prompt ### Creation ```bash theme={null} prime disks create \ --id 7d2232 --size 500 \ --name ml-training-data ``` > [!NOTE] > > Disks are billed continuously from creation until termination. Make sure to terminate disks when no longer needed. ## Listing Your Disks View all your disks: ```bash theme={null} prime disks list ``` ### Output Options Display as JSON: ```bash theme={null} prime disks list --output json ``` With pagination: ```bash theme={null} prime disks list --limit 50 --offset 0 ``` The output shows: * Disk ID, name, and size * Current status * Provider and location * Creation date and age * Hourly cost * Attached pods and clusters ## Getting Disk Details View detailed information about a specific disk: ```bash theme={null} prime disks get ``` Display as JSON: ```bash theme={null} prime disks get --output json ``` The detailed view includes: * Complete disk configuration * Current status and pricing * List of attached pods * List of attached clusters * Creation and update timestamps * Location information ## Updating a Disk Update the disk name: ```bash theme={null} prime disks update --name new-disk-name ``` > [!NOTE] > > Currently, only the disk name can be updated. Disk size cannot be changed after creation. ## Terminating a Disk Terminate a disk when you no longer need it: ```bash theme={null} prime disks terminate ``` Skip confirmation prompt: ```bash theme={null} prime disks terminate --yes ``` > [!WARNING] > > **Disk termination is permanent and irreversible**. All data stored on the disk will be lost. Make sure to backup any important data before terminating a disk. > [!NOTE] > > **Detachment Timing**: After terminating an instance with attached disks, you may need to wait a few moments for the disk to detach before you can terminate it or attach it to another instance. ## Disk Status Values Disks go through different statuses during their lifecycle: | Status | Description | | - | - | | `PROVISIONING` | Disk is being created | | `PENDING` | Disk status is changing | | `ACTIVE` | Disk is ready and can be attached to instances or terminated | | `STOPPED` | Disk is stopped | | `ERROR` | An error occurred during disk operations | | `DELETING` | Disk is being terminated | | `TERMINATED` | Disk has been terminated | | `UNKNOWN` | Disk status is unknown (cannot access the current status) | ## Using Disks with Instances ### Attach Disks During Instance Creation ```bash theme={null} prime pods create \ --id 346663 \ --disks disk-id-1 \ --disks disk-id-2 ``` Learn more in [Provision Instance](/cli-reference/provision-gpu). ### Best Practices **Location Planning** * Create disks in the same datacenter where you plan to run GPU instances * Use `prime availability disks` to find compatible storage options **Size Planning** * Start with the size you need - you cannot resize disks after creation * Consider future growth when selecting disk size * Remember that larger disks cost more per hour **Data Management** * Use descriptive names to identify disk contents * Regularly backup important data from disks * Terminate unused disks to avoid unnecessary costs **Multinode Disks** * Use multinode-capable disks for distributed training * Check the `isMultinode` field in availability output * Ensure your provider supports simultaneous access if needed ## Common Use Cases ### Shared Training Data Create large disks with your training datasets that can be reused across multiple training runs: ```bash theme={null} # Create a 1TB disk for training data prime disks create --size 1000 --name training-datasets --id 7d2232 # Attach to multiple training instances prime pods create --id gpu-id --disks disk-id-1 ``` ### Model Checkpoints Use disks to persist model checkpoints and training artifacts: ```bash theme={null} # Create disk for checkpoints prime disks create --size 200 --name model-checkpoints --id 7d2232 # Use with training instance prime pods create --id gpu-id --disks checkpoint-disk-id ``` ### Multi-Node Distributed Training Use multinode-compatible disks for distributed training: ```bash theme={null} # Find multinode-capable disk storage prime availability disks --regions united_states # Create multinode disk (look for "Yes" in Is Multinode column) prime disks create --size 500 --name shared-data --id multinode-disk-id # Attach to multiple nodes prime pods create --id gpu-id-1 --disks shared-disk-id prime pods create --id gpu-id-2 --disks shared-disk-id ``` ## Related Documentation * [Check Availability](/cli-reference/check-gpu-availability) - Find available disk options * [Provision Instance](/cli-reference/provision-gpu) - Attach disks when creating instances * [API Disk Management](/api-reference/managing-disks) - API reference for disk operations # Provision Instance Source: https://docs.primeintellect.ai/cli-reference/provision-gpu How to provision an instance using availability data > [!NOTE] > > Before you start, ensure that you have [API Key](/api-reference/api-keys) with `Instances -> Read and write` permission ## Checking GPU Availability First, check available GPU configurations: ```bash theme={null} prime availability list ``` > [!TIP] > > Filter results using these options: > > * `--gpu-type`: Specific GPU model (e.g., H100\_80GB) > * `--regions`: Geographic locations (e.g., united\_states,canada) > * `--gpu-count`: Number of GPUs needed > * `--socket`: Socket type (PCIe, SXM4, etc.) > * `--provider`: Name of a provider ## Creating an Instance Create a new instance interactively: ```bash theme={null} prime pods create ``` ### Available Options **--id** `string` Short ID from availability list **--cloud-id** `string` Cloud ID from provider **--gpu-type** `string` GPU model (e.g., H100\_80GB, A100) **--gpu-count** `integer` Number of GPUs required **--name** `string` Instance name (alphanumeric and dashes only) **--disk-size** `integer` Storage size in GB **--vcpus** `integer` Number of virtual CPUs **--memory** `integer` RAM in GB **--image** `string` Operating system image **--team-id** `string` Team identifier for the instance **--env** `string` Environment Variable in form `KEY=value` - this can be used multiple times **--disks** `string` Attach existing disk IDs to the instance. Can be specified multiple times for multiple disks. * Example: `--disks disk-id-1 --disks disk-id-2` **--share-with-team** `boolean` Share the instance with all team members. Requires a team to be set via `--team-id` or `prime config set-team-id`. Team members' SSH keys will be added to the instance. **--add-members** `boolean` Interactively select specific team members to share the instance with. Displays a numbered list of team members and prompts for selection. Enter comma-separated numbers or `all` for everyone. ### Interactive Creation Process If options are not provided, the CLI will guide you through: 1. GPU Selection 2. Configuration Options 3. Resource Allocation 4. Image Selection 5. Team Assignment > [!NOTE] > > The CLI will show default values and valid ranges for resources like disk size, vCPUs, and memory based on the selected GPU configuration. ### Attaching Persistent Disks To attach existing persistent disks to your instance: ```bash theme={null} prime pods create \ --id 346663 \ --disks disk-id-1 \ --disks disk-id-2 ``` This is useful for: * Accessing shared datasets across multiple instances * Persisting model checkpoints and training artifacts * Resuming training from previous sessions > [!TIP] > > Use `prime disks list` to see your available disks and their IDs. Learn more about creating and managing disks in [Managing Disks](/cli-reference/managing-disks). ## Sharing with Team Members When working in a team, you can share instances so that other members' SSH keys are added to the instance, giving them direct access. ### Share with all team members ```bash theme={null} prime pods create --share-with-team ``` ### Interactively select members ```bash theme={null} prime pods create --add-members ``` This displays a numbered list of team members (excluding yourself) and prompts you to select which members to share with. Enter comma-separated numbers (e.g. `1,3,5`) or type `all` to share with everyone. ### Auto-share by default To always share new instances with your team without passing a flag each time: ```bash theme={null} prime config set-share-resources-with-team true ``` > [!NOTE] > > Sharing flags require a team to be configured. Set one with `prime config set-team-id` or pass `--team-id` when creating the instance. ## Managing Your Instance Monitor instance status: ```bash theme={null} prime pods status ``` SSH into your instance: ```bash theme={null} prime pods ssh ``` > [!WARNING] > > Configure your SSH key path in CLI settings and run chmod 400 on your private\_key.pm before using SSH. Terminate instance: ```bash theme={null} prime pods terminate ``` > [!NOTE] > > You'll be prompted to confirm before termination. # Teams Source: https://docs.primeintellect.ai/cli-reference/teams Manage and view your teams and team members ## List Your Teams View the teams you belong to: ```bash theme={null} prime teams list ``` ### Available Options **--limit** `integer` Maximum number of teams to return (default: 100) **--offset** `integer` Number of teams to skip for pagination (default: 0) **--output** `string` Output format: `table` (default) or `json` ### Example Output | ID | Name | Slug | Role | Created | | - | - | - | - | - | | clvb0itli0000oxevorvgrpfn | My Team | my-team | ADMIN | 2025-01-15T10:30:00 | | clvb1jtmk0001pxfv1abc2def | Research Lab | research-lab | MEMBER | 2025-02-01T14:20:00 | ## List Team Members View the members of a specific team: ```bash theme={null} prime teams members ``` This uses the team from your config. To specify a different team: ```bash theme={null} prime teams members --team-id clvb0itli0000oxevorvgrpfn ``` ### Available Options **--team-id** `string` Team ID to list members for. Uses the configured team ID if not specified. **--output** `string` Output format: `table` (default) or `json` ### Example Output | User ID | Name | Email | Role | Joined | | - | - | - | - | - | | clvb0itli0000oxevorvgrpfn | Alice | [alice@example.com](mailto:alice@example.com) | ADMIN | 2025-01-15T10:30:00 | | clvb1jtmk0001pxfv1abc2def | Bob | [bob@example.com](mailto:bob@example.com) | MEMBER | 2025-02-01T14:20:00 | > [!TIP] > > Set a default team with `prime config set-team-id` so you don't need to pass `--team-id` every time. # FAQ Source: https://docs.primeintellect.ai/faq Frequently Asked Questions ## Instance Types and Pricing > **What is an On-Demand instance?** > > An On-Demand instance is a non-interruptible virtual machine that you can deploy and terminate at any time, paying only for the compute time you use. > **What is a Spot instance?** > > A Spot instance is a cost-effective virtual machine that uses unused cloud capacity. It offers discounts of up to 90% compared to On-Demand instances, but it can be interrupted if the capacity is needed by other users. Spot instances are ideal for workloads that are flexible and can tolerate interruptions, such as batch jobs, data analysis, or fault-tolerant applications. > **How will I be charged for GPU instances?** > > Each Pod has an hourly cost depending on the provider and GPU type. Your Prime Intellect credits are deducted every minute while the Pod is active. If your credits run out, your Pods will be automatically deleted. We highly recommend setting up our auto-top-up feature in your [billing settings](https://app.primeintellect.ai/dashboard/billing) to ensure balances are automatically topped up as needed. ## Cloud Providers and Infrastructure > **Which cloud provider do you support?** > > We are constantly adding new providers! You can find the current list of providers in our app. > **How long does it take for instances to launch?** > > Instances typically launch within a few minutes, but the exact time may vary depending on the provider. > More detailed information on spin-up time is shown on your instance card. > **Is there an SLA or guaranteed uptime for my instances?** > > We don't provide formal SLAs at this time, as the underlying infrastructure relies on various providers. If you experience repeated downtime or unreliable performance, contact support. We'll work with the provider to resolve the issue or offer refunds/credits when disruptions stem from provider-side instability. For more reliability, choose Secure Cloud providers or verify uptime and past performance before deploying long-term workloads. ## Data Management and Storage > **How can I pause or stop my running instance without terminating it?** > > You can pause and resume instances on supported providers (currently Runpod), allowing you to save state and continue later at a lower cost than keeping the instance running. When resuming, note that GPU resources may not be immediately available, leading to wait times. Look for the "Pause" button on the instance details page. ⚠️ Important: If you terminate (rather than pause) your instance, you will lose all data. > **How do I ensure my data persists after pausing or restarting an instance?** > > For paused instances only, data can be persisted in a special directory that is shown in your instance details in the dashboard. For example, Runpod stores persistent data in the `/workspace` directory. Note that this only works when pausing an instance - if you terminate the instance, all data will be lost. Always back up critical data to external storage (e.g., `S3`) before terminating or pausing. > **Can my data be recovered once I've terminated my instance?** > > No, once an instance is terminated, all data stored on it is permanently deleted and cannot be recovered. > **Can I get very large storage volumes (multi-terabyte)?** > > While most providers let you configure storage during instance creation, very large volumes may not always be honored. If your requested storage isn't fully available or doesn't match what's provisioned, contact support. We'll verify the provider's limitations and may offer guidance or alternative providers that can meet larger volume requirements. ## Configuration and Connectivity > **Can I specify custom hardware configurations such as CPU, RAM, or disk size?** > > Yes. Many providers allow you to configure CPU, RAM, and storage during instance creation. However, some have limits. Hover over the pricing or configuration fields on the instance creation page to see what's available. We're also adding filters to search by CPU, RAM, and storage soon. > **I'm having trouble connecting via SSH or Jupyter. How do I fix connection issues?** > > First, ensure you have the correct SSH key from your instance's "SSH Connection" button and set permissions on your local machine correctly with `chmod 400 private_key.pem`. For Jupyter notebooks, simply click the provided link on your instance page to access the notebook interface directly in your browser. If you still face issues, contact support with error details. > **How can I see which ports are open on my instance?** > > You can click the Port Information button on your instance to see which ports are open: > > ![Click Port Information Button](https://mintcdn.com/primeintellect/u4gd9w_CirhPniY2/images/instance-running-click-port-info.png?fit=max\&auto=format\&n=u4gd9w_CirhPniY2\&q=85\&s=6c996bd5a6d8cb689511e72f4e32843b) > **Can I open custom ports or host a service accessible over HTTP/HTTPS on my instance?** > > Yes. Certain providers (e.g., Latitude, Cudo, Scaleway, Hyperstack, Oblivus, Massedcompute, Datacrunch) already have open ports. We're also adding the ability to specify custom port mappings. Check provider details or the FAQ to see if port configuration is supported. If not, you may need to wait for the upcoming port-exposure feature rollout. > **Can I run Docker or other system services on my instance?** > > Some providers support `docker-in-docker` configurations and system-level services, while others do not. Check the provider's documentation or contact support if you encounter issues. We're working on better clarifications and tutorials to help you understand what's supported in your chosen environment. ## Billing and Payments > **Is there a way to get a refund if my instance fails or I'm overcharged?** > > If your instance fails due to provider issues (such as failing to start, crashing early, or being impossible to terminate from your dashboard), contact support with the instance details. We typically provide refunds or credits when the issue stems from our system or provider instability. Please contact us in the support chat by typing REFUND as your first word, then describe your issue in detail and include screenshots. > **How do I apply a promotional code to get credits?** > > When you sign up or top up credits, look for the promo code input field. If you've already signed up, the promo code field is typically found in the billing/top-up section. If you have trouble finding it, contact support and we'll manually credit your account. > > ![Redeem Coupon](https://mintcdn.com/primeintellect/u4gd9w_CirhPniY2/images/redeem-coupon.jpg?fit=max\&auto=format\&n=u4gd9w_CirhPniY2\&q=85\&s=bd43dd52fcc402f4d295440bff6ed904) > **How do I set up team accounts or custom billing emails for invoicing?** > > We've introduced a "Teams" feature where you can invite multiple users and set a dedicated billing email. Create a team from your dashboard's team settings, add members, and specify the billing email (e.g., [billing@company.com](mailto:billing@company.com)) so that all invoices are sent there. This helps consolidate usage and manage costs for multiple users under one account. > **Can I pay in other currencies or use alternative payment methods?** > > Currently, most billing occurs in USD and via standard payment methods. If you need invoicing in other currencies or want to explore alternative digital payment options, please contact support. We're actively considering these features and may arrange manual invoicing or special payment methods in the future. ## Troubleshooting > **My instance won't start or is stuck in 'pending.' What should I do?** > > If your instance fails to launch or remains "pending" for an extended period, first try terminating it and creating a new one. If the problem persists, contact support with the instance details. In cases where the issue is on the provider's side, we can often provide refunds or credits. ## GPU Providers > **How do I become a GPU provider?** > > We are actively expanding our network of GPU providers, including both individual compute providers and larger cloud platforms. For more information, please contact us at [contact@primeintellect.ai](mailto:contact@primeintellect.ai). # Browser Environments Source: https://docs.primeintellect.ai/guides/browser-environments Guide to building and training browser-based environments with Prime Intellect and Browserbase. # Multimodal Browser Environments in verifiers **BrowserEnv now supports vision-based web browsing via Computer Use Agent (CUA) mode.** Both modes are modes in verifiers and we provide examples for trying both. This guide covers the two interaction modes, how to get started, and walks through two real environments: a lightweight eval (`bb_demo.py`) and a full benchmark for training (`webvoyager_no_anti_bot.py`). It also covers BrowserEnv-specific pieces of RL training, including DOM and CUA training configurations and how to integrate with Hosted Training. *** ## Two Modes of Browser Interaction `BrowserEnv` is a unified `StatefulToolEnv` subclass that supports two operating modes, selected via the `mode` parameter. Both modes use [Browserbase](https://browserbase.com) as the browser provider. ### DOM Mode: Natural Language Control DOM mode uses the [Stagehand](https://github.com/browserbase/stagehand) SDK to translate natural language instructions into browser actions. Stagehand runs its own LLM internally (configured via `stagehand_model`, defaults to `openai/gpt-4o-mini`) to interpret the page DOM and execute the appropriate operations. The agent's tool surface is high-level and semantic: * `navigate(url)`: go to a URL * `observe(instruction)`: find possible actions matching a natural language description * `act(instruction)`: execute an action described in natural language (click a button, fill a form) * `extract(instruction, schema_json)`: extract structured data from the page The agent never sees the rendered page. It works through Stagehand's abstraction of the DOM, which means it requires no coordinates, and no screenshots. This is fast and effective when pages have reliable semantic HTML. DOM mode works best when actionable page state is exposed semantically through Stagehand. Visually ambiguous cases, especially overlays or elements that are easier to disambiguate by pixels, can still be easier in CUA. **Stagehand routing:** When `proxy_model_to_stagehand=False` (default), Stagehand uses its own `stagehand_model` and `MODEL_API_KEY`. When `proxy_model_to_stagehand=True`, BrowserEnv injects the rollout client's model name, base URL, and API key into `observe`, `act`, and `extract`, so those Stagehand calls run through the same client/model endpoint as the rollout model. ### CUA Mode: Vision-Based Control (Multimodal) CUA mode gives the agent a live screenshot of the rendered page after every action. The agent sees pixels and acts via screen coordinates, the same way a human would interact with a screen. The agent's tool surface is low-level and coordinate-based: * `click(x, y, button)`: click at screen coordinates * `double_click(x, y)`: double-click at coordinates * `type_text(text)`: type text into the focused element * `keypress(keys)`: press keyboard keys * `scroll(x, y, scroll_x, scroll_y)`: scroll at a position * `goto(url)`: navigate to a URL * `back()` / `forward()`: browser history navigation * `wait(time_ms)`: wait for a specified duration * `screenshot()`: capture the current page state Each action returns a multimodal response: a text status block (URL, viewport dimensions, success/error) and a base64 PNG screenshot encoded as an `image_url` content block. The model processes both. After the tool call returns, `BrowserEnv` moves screenshot parts out of tool messages into a trailing user message. When `keep_recent_screenshots` is set, older screenshots are replaced with `[Screenshot removed to save context]` placeholders. **When to use it:** Tasks that require visual understanding, such as navigating unfamiliar UIs, interacting with canvas-rendered apps, clicking elements that lack semantic markup, or any workflow where a human would need to *look* at the screen to proceed. ### Side-by-Side Comparison | Aspect | DOM Mode | CUA Mode | | - | - | - | | **Control paradigm** | Natural language via Stagehand | Vision-based screen coordinates | | **Observation space** | Text (DOM abstractions) | Multimodal (screenshots + text) | | **Server requirement** | None (Stagehand SDK direct) | CUA server (auto-deployed or manual) | | **Extra API key** | `MODEL_API_KEY` by default, or the rollout client when proxied | The CUA server template can forward `OPENAI_API_KEY` internally if it is present | | **Best for** | Structured web interactions | Visual/complex UIs | | **Speed** | Faster (direct DOM manipulation) | Slower (screenshot round-trips) | *** ## Getting Started ### Installation Both example environments require the `browser` extra: ```bash theme={null} # Install verifiers with browser support uv pip install "verifiers[browser]" ``` ### Environment Variables ```bash theme={null} # Required for both modes export BROWSERBASE_API_KEY="your-api-key" export BROWSERBASE_PROJECT_ID="your-project-id" # Required for LLM judge model - pick your favorite model/your custom judge model, here we use OpenAI as an example export OPENAI_API_KEY="your-openai-key" # Required for DOM mode only (Stagehand's internal LLM) - pick your favorite model provider export MODEL_API_KEY="your-llm-api-key" ``` ### Running the Examples Both examples ship with the same task: navigate to the Prime Intellect homepage and read the headline. Note to do the below, you need to have a prime account. ```bash theme={null} # Install both examples from the Environments Hub prime env install primeintellect/browser-dom-example primeintellect/browser-cua-example # DOM mode uv run vf-eval browser-dom-example -m openai/gpt-4.1-mini # CUA mode (pre-built image, fastest and recommended) uv run vf-eval browser-cua-example -m openai/gpt-4.1-mini ``` *** ## CUA Mode Execution Backends CUA mode requires a **CUA server** — a lightweight TypeScript HTTP service that bridges the Python browser agent and the browser. Under the hood, Browserbase uses [Understudy](https://github.com/browserbase/stagehand/tree/main/packages/core/lib/v3/understudy), a custom and powerful Chrome DevTools Protocol (CDP) driver developed at Browserbase as part of [Stagehand](https://github.com/browserbase/stagehand), the AI browser automation framework. Understudy was built to overcome limitations in Playwright's built-in automation capabilities (e.g. more granular input control and richer session introspection). Because Understudy's CDP engine is written in TypeScript, a thin server layer is needed to expose it as a REST API that the Python environment can call. The CUA server is that layer — it receives coordinate-based action requests over HTTP, translates into CDP commands via Understudy, and returns screenshots back to the agent. Three execution modes are available, from fastest to most flexible: ### 1. Pre-built Docker Image (Default) Uses `deepdream19/cua-server:latest` with everything pre-installed. Startup takes \~5–10 seconds. This is the recommended approach. ```bash theme={null} uv run vf-eval browser-cua-example -m openai/gpt-4.1-mini -b https://api.openai.com/v1 -k OPENAI_API_KEY ``` ### 2. Binary Upload Builds and uploads a custom CUA server binary to a sandbox at runtime. Startup takes \~30–60 seconds. Use this when you need a modified server. ```bash theme={null} uv run vf-eval browser-cua-example -m openai/gpt-4.1-mini -a '{"use_prebuilt_image": false}' ``` ### 3. Manual Server (Local Development) Connect to a locally running CUA server. Start the server yourself, then point the environment at it: ```bash theme={null} # Terminal 1: start the CUA server cd assets/templates/browserbase/cua && pnpm dev # Terminal 2: run with sandbox disabled uv run vf-eval browser-cua-example -m openai/gpt-4.1-mini -a '{"use_sandbox": false}' ``` ### Screenshot Management CUA mode captures a screenshot after every action. Two parameters control how screenshots flow: * `save_screenshots` (bool): persist every screenshot to disk as timestamped PNGs in `screenshot_dir` (defaults to `./screenshots`). The CUA example defaults this to `False`; the `BrowserEnv` class itself defaults to `True`. * `keep_recent_screenshots` (int | None): how many recent screenshots to retain in the conversation context window sent to the model. Defaults to `2`. Set to `None` to keep all (higher token cost). *** ## Example Environments ### [`bb_demo`](https://app.primeintellect.ai/dashboard/environments/prime/bb-demo): Quick Eval with Click Visualization `bb_demo.py` is a lightweight single-task environment designed for quick CUA mode evaluation and debugging. It serves as a hello world for multimodal browser environments and pairs a simple browsing task with a custom `CUAMode` subclass that annotates saved screenshots with click markers. ![bb\_demo eval rollout](https://mintcdn.com/primeintellect/14whN4o7A3u5_UrF/guides/images/bb_demo_eval.png?fit=max\&auto=format\&n=14whN4o7A3u5_UrF\&q=85\&s=d510e01b28abb6f3304a982232d8cf84) **Task.** The agent is asked to navigate to the Prime Intellect website, find the blog page, and summarize the latest post. A `JudgeRubric` backed by `gpt-4.1-mini` evaluates whether the agent's response adequately describes the blog content, returning `1.0` for "yes" and `0.0` otherwise. **Click marker overlay.** The environment subclasses `CUAMode` as `ClickMarkerCUAMode`, which overrides `click()` and `double_click()` to store the target (x, y) coordinates before each action. When the resulting screenshot is saved to disk, `_save_screenshot_with_marker()` uses Pillow to composite a visual marker (concentric circles, crosshairs, coordinate label) onto the PNG. The marker is drawn on the *saved* file only. The screenshot returned to the model in conversation context is unmodified, keeping the agent's visual input clean while giving developers an annotated record of every click. **Subclassing pattern.** `ClickMarkerBrowserEnv` intercepts `BrowserEnv.__init__` when `mode="cua"`. It manually extracts all CUA-specific parameters from `kwargs`, bypasses the parent's mode setup by calling `StatefulToolEnv.__init__` directly, then constructs and registers the custom `ClickMarkerCUAMode`. For DOM mode, it delegates entirely to the parent. This pattern is useful any time you need to swap in a custom mode implementation without forking `BrowserEnv`. **Usage:** ```python theme={null} from bb_demo import load_environment env = load_environment( save_screenshots=True, screenshot_dir="./my_screenshots", mark_clicks=True, click_marker_radius=15, ) ``` Requires `uv pip install Pillow`. Without it, screenshots still save without markers and the environment shows a warning at startup. ### [`webvoyager_no_anti_bot`](https://app.primeintellect.ai/dashboard/environments/browserbase/webvoyager-no-anti-bot): Training-Scale Benchmark WebVoyager is a 600-task web navigation benchmark spanning real websites (Allrecipes, Amazon, Apple, ArXiv, GitHub, Google Flights/Maps/Search, ESPN, and more). This version filters out 43 tasks from `dictionary.cambridge.org` that are blocked by Cloudflare anti-bot protection, leaving 93.3% of the original dataset intact. It supports both DOM and CUA modes, making it a good candidate for RL training runs. Here is an [example training run](https://app.primeintellect.ai/training/shared/v8uz1wu40av5bssrqz3mi203) with Qwen3-VL-8B-Instruct: ![WebVoyager training run](https://mintcdn.com/primeintellect/14whN4o7A3u5_UrF/guides/images/webvoyager_training.png?fit=max\&auto=format\&n=14whN4o7A3u5_UrF\&q=85\&s=da6307484ec0a917aa8ca746d322a588) **Dataset.** Tasks are loaded from a local JSONL file (`WebVoyager_data_clean.jsonl`). Each row has a natural language task (`ques`), a starting URL (`web`), a website name (`web_name`), and a task ID. There are no ground-truth answers because WebVoyager is task-completion-based rather than answer-matching. You can filter by website with `web_filter` (e.g., `web_filter="Amazon"`) and limit the number of examples with `num_examples`. **Evaluation.** Since there are no explicit answers, the environment uses a task-completion judge. The agent's entire multi-turn trajectory is rendered into a structured text transcript by `WebVoyagerTrajectoryParser`, a custom `vf.Parser` subclass. This transcript includes assistant messages, tool calls with normalized arguments, and truncated tool results, while images are excluded. The transcript is capped at 12,000 characters to keep judge context manageable. The judge prompt instructs the LLM to verify that the agent navigated to the correct site, performed the required actions, and reached the requested end state. If the agent made no tool calls at all, the reward is automatically `0.0` without consulting the judge. **Transcript rendering.** `render_webvoyager_transcript()` walks the completion messages and emits a line-by-line log: `ASSISTANT:` for text, `TOOL_CALL name({args})` for tool invocations, and `TOOL_RESULT:` for tool responses (truncated to 500 characters each). This keeps the judge grounded in what actually happened rather than what the agent claims happened. The judge prompt explicitly says to treat unsupported assertions as insufficient evidence. **Mode flexibility.** `load_environment()` accepts `mode="dom"` or `mode="cua"` and passes all relevant configuration through to `BrowserEnv`. Both modes work against the same dataset and rubric. **Usage:** ```bash theme={null} # Install the environment prime env install browserbase/webvoyager-no-anti-bot # All 600 tasks, DOM mode uv run vf-eval webvoyager-no-anti-bot -m openai/gpt-4.1-mini # CUA mode, filtered to Amazon tasks uv run vf-eval webvoyager-no-anti-bot -m openai/gpt-4.1-mini -a '{"mode": "cua", "web_filter": "Amazon"}' # 10 examples for a quick test uv run vf-eval webvoyager-no-anti-bot -m openai/gpt-4.1-mini -a '{"num_examples": 10}' ``` Note: the full 600-task suite takes a while to run. For initial testing, use `num_examples` or `web_filter` to scope it down. *** ## Building Your Own Browser Environment Both examples follow the standard verifiers environment contract: a Python module exposing `load_environment(**kwargs) -> vf.Environment`. The pattern is: 1. **Define a dataset**: a HuggingFace `Dataset` with `question`, `answer`, and optionally `start_url` and `task_id` columns. For task-completion benchmarks where there's no ground-truth answer (like WebVoyager), set `answer` to an empty string and rely on a task-completion judge. 2. **Define a rubric**: typically a `JudgeRubric` with an LLM judge. For answer-matching tasks, the judge compares agent output to the expected answer. For task-completion tasks, subclass `vf.Parser` to render the trajectory into a judge-friendly transcript and evaluate whether the task was actually done. 3. **Construct a `BrowserEnv`**: pass `mode`, dataset, rubric, system prompt, and any mode-specific configuration. 4. **Package it**: add a `pyproject.toml` with `verifiers[browser]>=0.1.8` as a dependency. ```python theme={null} import verifiers as vf from verifiers.envs.integrations.browser_env import BrowserEnv from datasets import Dataset def load_environment(max_turns: int = 15, **kwargs) -> vf.Environment: dataset = Dataset.from_dict({ "question": ["What is the title of the first blog post?"], "answer": ["Expected answer here"], "start_url": ["https://example.com/blog"], }) rubric = vf.JudgeRubric(judge_model="gpt-4o-mini", judge_prompt="...") rubric.add_reward_func(your_judge_func, weight=1.0) return BrowserEnv( mode="cua", # or "dom" dataset=dataset, rubric=rubric, max_turns=max_turns, system_prompt="...", **kwargs, ) ``` Then install and evaluate: ```bash theme={null} uv pip install -e ./environments/your_env uv run vf-eval your-env -m openai/gpt-4.1-mini ``` *** ## RL Training with Browser Environments Browserbase integrates with Prime Intellect through `BrowserEnv`. Browserbase handles the cloud browsers, `BrowserEnv` wraps those sessions as a verifiers environment, and Prime runs the evaluation or RL training loop. In practice, that means the same BrowserEnv configuration can be used in both evaluation and training. The training-specific questions are usually: * whether you want `mode="dom"` or `mode="cua"` * whether Stagehand should keep its own model or be routed through the rollout model * which browser/session settings you want in the `[[env]]` config ### Before You Train 1. Validate the environment with evals first. Reward quality matters more than running a large training job quickly. 2. Check reward distribution. If everything is `0.0` or everything is `1.0`, the run will not teach you much. 3. Start with a small training run before scaling up browser minutes and GPU time. ### Training Target in This Repo `webvoyager_no_anti_bot` is the training-scale BrowserEnv example in this repo. * 600 tasks in the filtered WebVoyager dataset * supports both `mode="dom"` and `mode="cua"` * uses task-completion reward rather than answer matching * renders assistant text, tool calls, and truncated tool results into the judge transcript * returns `0.0` reward immediately if a rollout makes no tool calls That makes it the cleanest local reference for comparing DOM and CUA training without changing the benchmark itself. Example Prime DOM training run: [WebVoyager RL run](https://app.primeintellect.ai/training/shared/aixtf0gfgstmasfsugb9x3bg). Example Prime CUA training run: [WebVoyager RL run](https://app.primeintellect.ai/training/shared/v8uz1wu40av5bssrqz3mi203). ### DOM Training A DOM sample run can be done with the following configuration: * model: `Qwen/Qwen3-4B-Instruct-2507` * `batch_size = 64` * `rollouts_per_example = 8` * environment id: `browserbase/webvoyager-no-anti-bot` By default, Stagehand keeps using its own `stagehand_model` and `MODEL_API_KEY`. If you want the trained rollout model to also handle Stagehand's DOM operations, enable `proxy_model_to_stagehand=true`. In that configuration, `observe`, `act`, and `extract` are routed through the same client/model endpoint as the rollout model. The checked-in sample does not add extra BrowserEnv args. To enable shared Stagehand routing, pass `mode = "dom"` and `proxy_model_to_stagehand = true` as taskset arguments for `browserbase/webvoyager-no-anti-bot`, and supply `MODEL_API_KEY` at dispatch with `-e`. **Suggested starting point for DOM mode:** a 4B-class instruct model, `max_steps = 100`, `batch_size = 64`, `group_size = 8`, `max_completion_tokens = 1024`. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). ### CUA Training A vision-language sample run can be done with the following configuration: * model: `Qwen/Qwen3-VL-4B-Instruct` * `batch_size = 32` * `rollouts_per_example = 4` * environment id: `prime/webvoyager-no-anti-bot` * BrowserEnv args: `mode = "cua"`, `max_turns = 10`, `viewport_width = 800`, `viewport_height = 600`, `keep_recent_screenshots = 2`, `memory_gb = 6` **Suggested starting point for CUA mode:** a 4B-class VL model, `max_steps = 100`, `batch_size = 32`, `group_size = 4`, `max_completion_tokens = 512`, with the BrowserEnv args listed above passed as taskset arguments. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). ### Credentials and Workflow Notes * Always provide `BROWSERBASE_API_KEY` and `BROWSERBASE_PROJECT_ID` when BrowserEnv is creating Browserbase sessions. * DOM mode needs `MODEL_API_KEY` unless you route Stagehand through the rollout model with `proxy_model_to_stagehand=true`. * CUA mode uses the same BrowserEnv backend choices as evaluation. The current bundled CUA server template forwards `OPENAI_API_KEY` into Stagehand if it is present, but that is a server-template detail rather than a separate BrowserEnv training argument. * Browserbase credentials belong in environment variables or training secrets, not in the TOML file. * The same BrowserEnv args used with `vf-eval -a '{...}'` belong in the training config under `[[env]]` `args = { ... }`. * BrowserEnv does not expose sandbox authentication as a constructor argument. Both Hosted Training and self-managed `prime-rl` use the same BrowserEnv-side mode choice and environment args. The difference is in how the training job is orchestrated, not in how BrowserEnv behaves. ### Picking DOM vs CUA for Training | Question | DOM Mode | CUA Mode | | - | - | - | | What does the model observe? | Semantic browser actions through Stagehand | Screenshots plus tool status text | | What kind of model fits best? | Text models | Vision-language models | | How do Stagehand calls behave during training? | Separate `stagehand_model` by default, or shared rollout routing with `proxy_model_to_stagehand=true` | Not applicable to the agent-side tool surface | | What usually matters most? | Semantic reliability and cheaper context | Visual grounding and UI disambiguation | ### Performance Notes * Browser training rollouts are slower than text-only environments because each rollout step interacts with a real browser session. * CUA mode is heavier than DOM mode because screenshots must be rendered, carried in context, and consumed by the model. * If your task does not need visual grounding, DOM mode is usually the faster and cheaper place to start. *** ## Quick Reference ### DOM Mode Arguments | Argument | Default | Description | | - | - | - | | `project_id` | Required | Browserbase project ID | | `browserbase_api_key_var` | `"BROWSERBASE_API_KEY"` | Env var for Browserbase API key | | `stagehand_model` | `"openai/gpt-4o-mini"` | Model for Stagehand DOM operations | | `model_api_key_var` | `"MODEL_API_KEY"` | Env var for Stagehand's API key | | `max_turns` | `10` | Max conversation turns | | `proxy_model_to_stagehand` | `False` | Route Stagehand LLM calls through eval model | ### CUA Mode Arguments | Argument | Default | Description | | - | - | - | | `use_sandbox` | `True` | Auto-deploy CUA server to sandbox | | `use_prebuilt_image` | `True` | Use pre-built Docker image (fastest) | | `prebuilt_image` | `"deepdream19/cua-server:latest"` | Docker image for sandbox | | `server_url` | `"http://localhost:3000"` | CUA server URL (when `use_sandbox=False`) | | `viewport_width` | `1024` | Browser viewport width | | `viewport_height` | `768` | Browser viewport height | | `save_screenshots` | `True` | Persist screenshots to disk | | `keep_recent_screenshots` | `2` | Screenshots in model context (`None` = all) | | `max_turns` | `15` | Max conversation turns | | `env` | `"BROWSERBASE"` | Browser provider (`"LOCAL"` or `"BROWSERBASE"`) | | `proxies` | `False` | Enable Browserbase proxies | | `advanced_stealth` | `False` | Enable anti-bot detection stealth mode | | `cpu_cores` | `2` | Sandbox CPU cores | | `memory_gb` | `4` | Sandbox memory | ### CUA Execution Modes | Mode | Flag | Startup | Use Case | | - | - | - | - | | Pre-built image | *(default)* | \~5-10s | Production | | Binary upload | `use_prebuilt_image=false` | \~30-60s | Custom server | | Manual server | `use_sandbox=false` | Instant | Local dev | # Overview Source: https://docs.primeintellect.ai/guides/index Step-by-step guides for training runs, environments, and agent workflows. Practical walkthroughs for common workflows. Start with training recipes for reusable patterns, or follow a full build-and-train guide for a concrete end-to-end project. > **[Your First Model Training](/guides/rl-training)** > > End-to-end walkthrough from workspace setup to Hosted Training and deployment. > **[Training Recipes](/guides/recipes)** > > Reusable RL patterns for math, code generation, tool use, and more. > **[Search Agents](/guides/search-agents)** > > Build document-search environments and train them end to end. # Training Recipes Source: https://docs.primeintellect.ai/guides/recipes Practical RL training recipes for math reasoning, code generation, tool use, and more. Each example here covers a common RL use case: what kind of environment to build, a minimal working implementation, a training config, and practical tips. Use the environment code as a starting point for your own runs on [Prime Intellect](/platform-overview). If you haven't launched a training run yet, start with the [Getting Started](/hosted-training/getting-started) guide first. *** ## Math Reasoning Train models to solve mathematical problems step-by-step, using symbolic verification to reward correct answers. **Environment type:** `SingleTurnEnv` with `MathRubric` **Why RL works here:** Models learn to produce correct final answers through trial and error. The reward signal is binary and cheap to compute — symbolic math verification checks whether the model's `\boxed{}` answer matches the ground truth, without needing an LLM judge. **Example environment:** ```python theme={null} import verifiers as vf from datasets import load_dataset def load_environment(split: str = "train", num_examples: int = -1) -> vf.Environment: ds = load_dataset("openai/gsm8k", split=split) dataset = vf.Dataset.from_hf(ds, question_col="question", answer_col="answer") if num_examples > 0: dataset = dataset.select(range(num_examples)) rubric = vf.MathRubric() return vf.SingleTurnEnv( dataset=dataset, rubric=rubric, system_prompt="Solve the problem step by step. Put your final answer in \\boxed{}.", ) ``` **Suggested starting point:** a 4B-class instruct model, `max_steps = 200`, `batch_size = 256`, `group_size = 8`, `max_completion_tokens = 1024`. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). **Tips:** * Start with GSM8K for validation — baseline models typically score 40–70%, leaving room for improvement. * For harder tasks (AIME, competition math), use a larger model like `Qwen/Qwen3-235B-A22B-Thinking-2507` and increase `max_tokens`. *** ## Code Generation with Sandboxes Train models to write correct code by executing their solutions in sandboxed environments and verifying outputs against test cases. **Environment type:** `PythonEnv` or `SandboxEnv` **Why RL works here:** The model gets a concrete pass/fail signal from running code. Unlike static checking, execution-based verification catches subtle bugs and rewards solutions that actually work. Multi-turn interaction lets the model iteratively debug when tests fail. **Example environment:** ```python theme={null} import verifiers as vf from datasets import Dataset def load_environment() -> vf.Environment: dataset = Dataset.from_list([ { "question": "Write a function `fibonacci(n)` that returns the nth Fibonacci number.", "info": '{"test_code": "assert fibonacci(0) == 0\\nassert fibonacci(1) == 1\\nassert fibonacci(10) == 55"}' }, # ... more examples ]) async def tests_pass(completion, info, state) -> float: code = completion[-1]["content"] test_code = info["test_code"] try: exec_result = state.get("exec_result", "") return 1.0 if "PASSED" in exec_result else 0.0 except Exception: return 0.0 rubric = vf.Rubric(funcs=[tests_pass]) return vf.PythonEnv( dataset=dataset, rubric=rubric, max_turns=5, ) ``` **Suggested starting point:** a 30B-A3B-class model, `max_steps = 300`, `batch_size = 256`, `group_size = 16`, `max_completion_tokens = 2048`. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). **Tips:** * Use `PythonEnv` for Python-specific tasks — it provides a persistent REPL that the model can use across turns. * Use `SandboxEnv` for multi-language tasks or when you need shell access. * Set `max_turns` to 3–5 to let the model iterate on failing test cases. * Consider a partial reward for passing some but not all tests, rather than all-or-nothing scoring. *** ## Multi-Turn Games and Puzzles Train models on interactive tasks where they must take actions over multiple turns, receiving feedback after each move. **Environment type:** Custom `MultiTurnEnv` subclass **Why RL works here:** Games provide dense, structured reward signals. The model learns strategies through repeated play — each rollout is a complete game, and the final score becomes the reward. Multi-turn structure naturally teaches planning and sequential decision-making. **Example environment (word guessing game):** ```python theme={null} import verifiers as vf from datasets import Dataset import random class WordGameEnv(vf.MultiTurnEnv): async def setup_state(self, state, **kwargs): state["target"] = state["info"]["target_word"] state["guesses"] = [] return await super().setup_state(state, **kwargs) async def env_response(self, messages, state): guess = messages[-1]["content"].strip().lower() target = state["target"] state["guesses"].append(guess) if guess == target: state["won"] = True return [{"role": "user", "content": "Correct! You found the word."}] # Give hints: which letters are in the right position hints = [] for i, (g, t) in enumerate(zip(guess, target)): if g == t: hints.append(f"Position {i+1}: correct") elif g in target: hints.append(f"Position {i+1}: wrong position, letter is in the word") else: hints.append(f"Position {i+1}: letter not in word") return [{"role": "user", "content": "\n".join(hints) + "\nGuess again."}] @vf.stop async def game_won(self, state): return state.get("won", False) def load_environment() -> vf.Environment: words = ["apple", "brain", "cloud", "dance", "eagle"] dataset = Dataset.from_list([ {"question": "Guess the 5-letter word. I'll give you hints after each guess.", "info": f'{{"target_word": "{w}"}}'} for w in words ]) async def win_reward(state) -> float: if state.get("won"): return max(0.2, 1.0 - 0.15 * len(state["guesses"])) return 0.0 rubric = vf.Rubric(funcs=[win_reward]) return WordGameEnv(dataset=dataset, rubric=rubric, max_turns=8) ``` **Suggested starting point:** a 4B-class instruct model, `max_steps = 100`, `batch_size = 128`, `group_size = 8`, `max_completion_tokens = 256`. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). **Tips:** * Games are excellent for validating your setup since they tend to show clear reward improvements within a small number of steps. * Shape rewards to be gradient-rich — instead of just 0/1 for win/loss, give partial credit (e.g., reward based on number of turns taken to win). * The built-in `alphabet-sort` environment is a great starting point — install it with `prime env install primeintellect/alphabet-sort`. *** ## Tool Use and Agentic Tasks Train models to use tools effectively — calling the right tool with the right arguments to accomplish a goal. **Environment type:** `ToolEnv` or `MCPEnv` **Why RL works here:** Tool use requires the model to reason about which tool to call, compose correct arguments, interpret results, and decide on next steps. RL training lets the model learn this decision-making loop through practice, improving both tool selection and argument construction. **Example environment (research assistant with search):** ```python theme={null} import verifiers as vf from datasets import Dataset async def web_search(query: str) -> str: """Search the web for information. Args: query: The search query to look up. Returns: Search results as text. """ # your search implementation return await do_search(query) async def calculate(expression: str) -> str: """Evaluate a mathematical expression. Args: expression: A math expression to evaluate (e.g. "2 + 2 * 3"). Returns: The result of the evaluation. """ try: return str(eval(expression)) except Exception as e: return f"Error: {e}" def load_environment() -> vf.Environment: dataset = Dataset.from_list([ { "question": "What is the population of France divided by the population of Switzerland?", "answer": "approximately 8.3" }, # ... more examples requiring tool use ]) async def answer_quality(completion, answer, judge) -> float: verdict = await judge(completion, answer) return 1.0 if "correct" in verdict.lower() else 0.0 rubric = vf.JudgeRubric(judge_model="gpt-4.1-mini") rubric.add_reward_func(answer_quality) return vf.ToolEnv( dataset=dataset, tools=[web_search, calculate], rubric=rubric, max_turns=10, ) ``` **Suggested starting point:** a 30B-A3B-class model, `max_steps = 200`, `batch_size = 256`, `group_size = 16`, `max_completion_tokens = 1024`. Pass API keys at dispatch with `-e` / `--env-file`. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). **Tips:** * Use `JudgeRubric` with an LLM judge for open-ended tasks where exact matching isn't feasible. * Store API keys for external services (judge models, search APIs) in a `secrets.env` file and reference it with `env_file`. * Monitor tool call counts via the automatic metrics — if the model isn't calling tools, the task may need a clearer prompt. * `MCPEnv` is useful when your tools are already implemented as MCP servers. *** ## Multi-Environment Training Train a single model on multiple tasks simultaneously to improve generalization. **Why RL works here:** Training on diverse tasks prevents the model from overfitting to a single task's reward surface. The model learns transferable skills (reasoning, tool use, instruction following) that improve performance across all tasks. **Suggested starting point:** a flagship MoE model, `max_steps = 500`, `batch_size = 512`, `group_size = 16`, `max_completion_tokens = 2048`, one `[[orchestrator.train.source]]` block per environment, plus `[wandb]` and `[eval]`. Set `ratio` on each source to weight the mix. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). **Tips:** * Run baseline evaluations on each environment before training to understand starting performance. * Use W\&B logging to compare per-environment reward curves during training. *** ## Workflow Summary Regardless of the use case, the typical Hosted Training workflow is: **1. Build your environment** Create an environment with a dataset, harness, and rubric using the [verifiers](/verifiers/overview) library. **2. Evaluate baseline** Run `vf-eval` against your environment (or `prime eval run --hosted` on the platform) to measure where the model starts. **3. Configure and launch training** Write a `.toml` config and launch with `prime train`. **4. Monitor and iterate** Watch reward curves on the dashboard, adjust your environment or config, and re-run. **5. Deploy** Download the trained weights from your [volume](/hosted-training/volumes) or the dashboard. > **[Getting Started](/hosted-training/getting-started)** > > Launch your first Hosted Training run in minutes. > **[End-to-End Run](/hosted-training/end-to-end-run)** > > Detailed walkthrough of a complete training run. > **[Environments](/verifiers/legacy/environments)** > > Learn how to build custom environments with verifiers. > **[Config Reference](/prime-rl/configuration)** > > Multi-environment training, evals, and more. # Your First Model Training Source: https://docs.primeintellect.ai/guides/rl-training Go from idea to training run to deployed agent. No programming or RL experience required. In this guide, we’ll walk you through setting up your workspace, creating your first agent environment, using it to evaluate baseline performance, launching a Hosted Training run, and deploying your model for inference. These instructions are intended for use on a Mac or Linux CPU development environment. No previous experience with RL is required. In fact, experience with *coding* isn’t even required — we’ll use agents for everything. We’ll just assume that you have Claude Code, Codex, Cursor, OpenCode, Amp, or some other similar coding agent installed on your computer. ## Setting Up Your Lab Workspace Ensure you have `uv` installed for managing Python packages: ```bash theme={null} curl -LsSf https://astral.sh/uv/install.sh | sh ``` Install the `prime` CLI: ```bash theme={null} uv tool install prime ``` Choose a folder on your machine as your Lab workspace (e.g. `~/dev/my-lab`) and do: ```bash theme={null} prime lab setup ``` This command prepares your workspace with: > **Python project bootstrap** > > Creates a Python project and installs `verifiers` for environment development. > **Coding-agent setup** > > Configures your workspace for coding-agent workflows. > **Instruction files** > > Downloads agent instruction files like `AGENTS.md` and Agent Skills. > **Starter configs** > > Downloads example training and evaluation configs. ```text theme={null} ~/dev/demo prime lab setup Supported coding agents: codex, claude, cursor, opencode, amp Primary coding agent [codex]: Using multiple coding agents? [y/N]: No pyproject.toml found, initializing uv project... Running: uv init Initialized project `demo` Running: uv add verifiers ... ... # install + download outputs omitted ... [................................................................................] 1371 / 1371 Downloaded configs/rl/wordle.toml from https://github.com/primeintellect-ai/verifiers +------------------------------------------ get started -------------------------------------------+ | | | idea -> environment -> eval -> training | | | | +-------------------------------- ask codex ---------------------------------+ | | | | | | | I want to train a model for . Propose an initial | | | | environment scaffold including relevant tools, generate a small | | | | synthetic dataset, run a quick eval baseline, inspect the results, | | | | and decide how to iterate on refining the implementation. | | | | | | | +----------------------------------------------------------------------------+ | | | | +-------------------- quick commands --------------------+ | | | | | | | $ uv run vf-init my-env | | | | $ uv run vf-eval my-env -m openai/gpt-5.4-nano -n 5 | | | | $ prime train configs/rl/qwen.toml | | | | $ uv run vf-gepa my-env -m openai/gpt-5.4-nano | | | | | | | +--------------------------------------------------------+ | | | +--------------------------------------------------------------------------------------------------+ ``` > [!TIP] > > Use one Lab workspace per research project and version it with Git. A workspace can contain multiple environments, configs, scripts, data, and eval outputs. ## Prompting Your Coding Agent For many low-to-medium complexity environments, we find that the latest coding agents are often capable of “one-shotting” them, when equipped with the provided context from `prime lab setup` and given a sufficiently detailed prompt. Providing the prompt below to a frontier coding agent (OpenCode + Codex 5.3) resulted in a fully functional environment for a calendar scheduling agent: > [!NOTE] > > Save this as `prompt.md` and pass it directly to your coding agent as your initial task prompt. ```md prompt.md wrap theme={null} Make an environment for a calendar scheduling agent. In each task, there should be a set of people with busy calendars, and individual + global constraints for scheduling the meeting. Some constraints can be "hard" (not allowed to violate), others can be "soft", where violating a constraint incurs some utility cost for certain attendees. Each attendee has a utility for the proposed meeting time between 0 and 1, and the task score will be the weighted average of attendee scores if an acceptable meeting time is found, and 0 otherwise. Attendee importance weights should be normalized to 1 for each task. We should be able to programmatically generate task problems, and deterministically validate that satisfying solutions exist (and what their best possible score would be). We should have fine-grained controls for key degrees of freedom in task generation, with higher-level parameters ("easy" / "medium" / "hard") for the full task set, which then map into setting ranges for the more fine-grained controls. Be creative, and use your judgment to design clean composition rules for converting meeting choices and conflicts into scores. Avoid complex branching/conditional logic where possible. Think carefully about designing your system in a way which discourages "backdoor" strategies or reward hacks. The best approach for an agent should be to make a good-faith effort to satisfy constraints as best as possible. Experiment with sampling strategies to ensure that tasks are solvable most of the time (so that we can pre-filter any unsolvable tasks cheaply), and that they aren't too easy -- there shouldn't be an abundance of valid solutions, random proposal times should be a bad strategy. Types of constraints we want to potentially account for: - Conflicting schedules - Time zones + early/late/day preferences - Meeting length - Room availability - Back-to-back meeting preferences - Desired-but-optional attendees - Other related constraints which reflect real-world calendar challenges Degrees of freedom: - Number of attendees - Window of consideration - Types of constraints - Tightness of constraints Use the StatefulToolEnv pattern, and in-memory data structures for the calendar + attendee information. The agent should have tools for things like: - Checking attendee calendars - Viewing attendee constraints - Checking score of a proposed window - Submitting a window The environment should have a max_turns parameter, and tool results should show the remaining turns to the agent. Default limit should be enough to allow reasonable exploration, but not so high that the agent can brute-force search all times. We should also have a nice standalone script in the environment which creates a TUI to visualize a "calendar problem" similar to typical meeting apps, including attendees, timeblocks, and constraints, but fully in the terminal, using Rich styling. Create a detailed design doc and plan for testing (PLAN.md), implement in full, revise PLAN.md after major milestones to reflect accomplishments and updated TODOs, and run basic small evals throughout as needed. You are welcome to use the PRIME_API_KEY set in my environment for inference tests (see configs/endpoints.toml for models). Let me know when you're happy with your implementation. ``` You can view the created environment in the Environments Hub: > **[prime/calendar-scheduling](https://app.primeintellect.ai/dashboard/environments/prime/calendar-scheduling)** > > Environments Hub We can use the visualizer script we asked our agent to make for viewing the environment task structure more directly: ```bash theme={null} ~/dev/demo (main*) uv run --project environments/calendar_scheduling calendar-scheduling-tui --show-oracle --difficulty medium --seed 5 Calendar Scheduling TUI ╭───────────────────────────────────────────────────── Calendar Scheduling Problem ─────────────────────────────────────────────────────╮ │ Task ID medium-209463 │ │ Difficulty medium │ │ Seed 209463 │ │ Window 5 days │ │ Candidate UTC hours 09:00 - 19:00 │ │ Meeting duration 90 minutes │ │ Score-check budget 6 │ │ Total candidates 90 │ │ Valid candidates 8 │ │ Oracle best score 0.7970 │ │ Random baseline 0.0630 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Attendees ┏━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ ID ┃ Name ┃ Type ┃ TZ ┃ Weight ┃ Preferred Day ┃ Preferred Local ┃ Hard Local ┃ ┡━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ attendee_1 │ Dakota │ optional │ UTC-2 │ 0.073 │ Day 1 (Tue Jan 06) │ 11.7-17.5 │ none │ │ attendee_2 │ Morgan │ optional │ UTC-7 │ 0.155 │ Day 1 (Tue Jan 06) │ 8.8-14.4 │ none │ │ attendee_3 │ Elliot │ required │ UTC+5 │ 0.072 │ Day 4 (Fri Jan 09) │ 10.9-16.6 │ none │ │ attendee_4 │ Avery │ required │ UTC+0 │ 0.146 │ Day 0 (Mon Jan 05) │ 12.5-17.0 │ none │ │ attendee_5 │ Parker │ required │ UTC-7 │ 0.180 │ Day 0 (Mon Jan 05) │ 10.7-16.4 │ 8.3-18.8 │ │ attendee_6 │ Robin │ required │ UTC-6 │ 0.185 │ Day 3 (Thu Jan 08) │ 10.2-14.7 │ none │ │ attendee_7 │ Reese │ optional │ UTC-1 │ 0.189 │ Day 4 (Fri Jan 09) │ 9.3-14.2 │ none │ └────────────┴────────┴──────────┴───────┴────────┴────────────────────┴─────────────────┴────────────┘ ╭───────────────────────────────────────────────────────────── Day Labels ──────────────────────────────────────────────────────────────╮ │ Day 0 (Mon Jan 05) | Day 1 (Tue Jan 06) | Day 2 (Wed Jan 07) | Day 3 (Thu Jan 08) | Day 4 (Fri Jan 09) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Availability Timeline (X busy, . free, =/# best window overlay) ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Lane ┃ Timeline ┃ ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ attendee_1 │ ........X...........|X..XXXXX..XXX.===...|....................|..XXX...............|.........XXXXX...... │ │ attendee_2 │ ............XXXXX...|..............===...|....................|..XXX...............|XXXXX............... │ │ attendee_3 │ ....................|.X............===...|.........XXXX.......|......X.............|XXX..X.............. │ │ attendee_4 │ ....................|..............===...|.....X.........X....|...............XXXXX|.................... │ │ attendee_5 │ ................XX..|..............===...|.XXXX...XXXX........|....................|.....X.............. │ │ attendee_6 │ .XXX............XXXX|..............===...|....................|.........XXXXX......|.....XX............X │ │ attendee_7 │ .XX.................|..XX.......XXX###...|...............XXXX.|....................|............XX...... │ │ room_1 (room) │ ....................|..........XXX.===X..|....................|....................|.................... │ └───────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ ╭────────────────────────────────────────────────────────── Constraint Model ───────────────────────────────────────────────────────────╮ │ Hard constraints │ │ - Required attendees must be able to attend │ │ - Hard local-time bounds cannot be violated │ │ - Chosen room must be available │ │ - Duration must match task duration exactly │ │ │ │ Soft utility penalties │ │ - Early/late local-time penalties │ │ - Day-preference distance penalties │ │ - Back-to-back penalty near busy blocks │ │ - Optional attendee absence penalty │ │ │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Oracle Best Windows ┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ Rank ┃ Window ┃ ┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ │ 1 │ Day 1 (Tue Jan 06) 16:00-17:30 UTC in room_1 │ └──────┴──────────────────────────────────────────────┘ ╭─────────────────────────────────────────────────────────── Oracle Summary ────────────────────────────────────────────────────────────╮ │ Best score: 0.7970 │ │ Valid windows: 8 / 90 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Legend: X busy, . free, = highlighted best free slot, # highlighted busy slot ``` ## Hosted Training After the environment is created, we prompt our agent to test performance more exhaustively, then to start a Hosted Training run using `Qwen/Qwen3-30B-A3B-Instruct-2507`, which is available via Hosted Training. ```text wrap=true theme={null} test with GPT-4.1 models, make sure we're seeing proper rollouts that succeed with non-zero scores. those models should be able to solve; if they can't we have a bug somewhere or need clearer instructions in the env. Once you feel good about the env, make a RL config and start a training run with bs 128, 8 rollouts per group, for the 30b instruct qwen model. ``` Available models can be viewed with: ```bash theme={null} prime train models ``` A config sets the model, the step and batch sizes, the environment to train on and its arguments, and optional `[wandb]` and `[eval]` blocks. For the current format and a worked example, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). Keys your environment needs go in a file you pass at dispatch rather than in the config: ```bash theme={null} # secrets.env OPENAI_API_KEY="${OPENAI_API_KEY}" WANDB_API_KEY="${WANDB_API_KEY}" ``` Runs can be started by running: ```bash theme={null} prime train configs/rl/calendar-scheduling.toml --env-file secrets.env --volume research ``` From the platform, you can view training curves, rollouts, configs, logs, and checkpoints. ![RL training curves](https://mintcdn.com/primeintellect/CIB0NGon5jBp6ynT/guides/images/rl-curves.png?fit=max\&auto=format\&n=CIB0NGon5jBp6ynT\&q=85\&s=2698a7c6b5ba477dc97e9c5abf1bdd8b) ![Rollout viewer](https://mintcdn.com/primeintellect/CIB0NGon5jBp6ynT/guides/images/rollout-viewer.png?fit=max\&auto=format\&n=CIB0NGon5jBp6ynT\&q=85\&s=1c8acfb3532aa01b1836e960b331fe39) Training runs are shareable. You can view this example run here: > **[calendar-sch--qwen3-30b-a3b-instru--wxhl0r](https://app.primeintellect.ai/training/shared/wxhl0r3gqmj6qxgpcg35m605)** > > Hosted Training ## Getting Your Model This walkthrough was recorded against the shared LoRA service, which produced a deployable adapter at the end of every run. That service has been [retired](/hosted-training/shared-lora-retired), and adapter deployment on Prime Inference has since been removed. For a Hosted Training run, add `[ckpt]`, dispatch with `--volume`, and retrieve the checkpoint with `prime volumes get` after the volume sync finishes. See [Checkpoint settings](/hosted-training/volumes#checkpoint-settings) for retention and cluster-specific requirements, and [Step 7 of the End-to-End Run](/hosted-training/end-to-end-run#step-7-review-results) for exporting weights to evaluate or serve. ## Trace storage and existing data Review [Prime Traces pricing and workspace upload settings](/traces/pricing-and-settings) before launching runs. For viewing and deleting stored traces, see [Managing traces](/traces/managing-traces). Export older evaluation and training samples using the [legacy archive workflow](/traces/legacy-storage) before November 6, 2026. # Training Search Agents Source: https://docs.primeintellect.ai/guides/search-agents Build three progressive RL environments for document search and run Hosted Training. Step-by-step walkthrough for training a model to search and reason over documents using RL. The example uses patents, but the same architecture applies to any document domain: legal filings, SEC documents, medical literature, enterprise knowledge bases. **Stack:** verifiers, ChromaDB, BigQuery, Hosted Training, Qwen3-4B, Llama-3.2-3B *** ## Environments Overview | Level | Name | Reward type | What the agent does | | - | - | - | - | | Level 1 | Metadata Retrieval | Exact match | Single tool call, parse structured response | | Level 2 | Multi-step Computation | Binary on deterministic answers | 2+ tool calls, arithmetic, cross-patent comparison | | Level 3 | Open-ended Analysis | LLM judge, normalized | Full patent text reading, synthesis, cross-patent reasoning | *** ## Prerequisites * Google Cloud account with BigQuery access. Free tier is sufficient; with that, you get 1 TB/month of queries. Downloading \~1,500 patents with full text uses \~3 GB. * A Prime Intellect account. You'll push environments to the Environments Hub and launch training runs from the CLI. * OpenAI API key, which is used for generating Level 3 Q\&A ground truth and the LLM judge at training time. * Python 3.10+ with `verifiers`, `chromadb`, `openai`, and the `prime` CLI. Install the Prime CLI with `uv tool install prime`. *** ## Step 1 - Get Patent Data Use Google's `patents-public-data` BigQuery dataset. It has full patent text (abstract, claims, description) and supports SQL filtering by company. ### BigQuery query ```sql theme={null} -- fetch_patents.sql SELECT publication_number, title, abstract, claims, description, filing_date, grant_date, ARRAY_LENGTH(claims_localized) AS claim_count FROM `patents-public-data.patents.publications` WHERE assignee_harmonized.name IN ( 'QUALCOMM INCORPORATED', 'ERICSSON', 'NOKIA', 'SAMSUNG', 'HUAWEI', 'SONY', ) AND country_code = 'US' AND grant_date IS NOT NULL LIMIT 1500 ``` Export the results to a GCS bucket or download directly as JSON. You can start with just \~1,500 patents; it produces meaningful training signal without excessive embedding costs. ### Format each patent Structure each patent as a markdown document and store metadata separately. Preserving document structure matters for Level 3 because the agent needs to navigate named sections. ```python theme={null} # format_patents.py def format_patent(row): doc = f"""# {row['title']} ## Abstract {row['abstract']} ## Claims {row['claims']} ## Description {row['description']} """ metadata = { "patent_id": row["publication_number"], "title": row["title"], "filing_date": row["filing_date"], "grant_date": row["grant_date"], "claim_count": row["claim_count"], } return doc, metadata ``` *** ## Step 2 - Design Three RL Environments The three levels impose a curriculum. For example, an agent that can't reliably call `get_metadata()` and parse a date string shouldn't attempt multi-patent technical comparisons. Each level introduces qualitatively harder capabilities. ### Level 1 - Single-tool metadata retrieval Questions that require exactly one tool call with no computation or reasoning | Question type | Example | | - | - | | Filing date | "When was patent X filed?" | | Grant date | "When was patent X granted?" | | Claim count | "How many claims does patent X have?" | | Title | "What is the title of patent X?" | **Dataset size:** 6,000 Q\&A pairs. Every answer is deterministically verifiable from the metadata. **Reward:** Binary - 1.0 for exact string match, 0.0 otherwise. **Tools available:** `search_patents(query)`, `get_metadata(patent_id)` Level 1 is a pipeline validation step. If reward doesn't climb toward 1.0 within 15 steps, something is wrong with your tool definitions, reward function, or data formatting. It isn't a model problem. ### Level 2 - Multi-step computation and comparison Questions requiring 2 or more tool calls followed by arithmetic or string comparison. | Question type | Example | What the agent does | | - | - | - | | Days to grant | "How many days between filing and grant for patent X?" | `get_metadata()` then parse two dates and subtract | | Filed first | "Which was filed first: X or Y?" | `get_metadata()` twice, then compare | | Claim difference | "How many more claims does X have than Y?" | `get_metadata()` twice, then subtract | | Search + retrieve | "Find a patent about SSB and tell me its filing date" | `search_patents()` then `get_metadata()` | | Abstract content | "Does the abstract of patent X mention '5G'?" | `get_abstract()` then string check | **Dataset size:** 500 Q\&A pairs. **Reward:** Binary on deterministic answers. For search questions with multiple valid answers, the reward function checks against a precomputed set of valid answers. **New tool:** `get_abstract(patent_id)` returns abstract text. The environment does not do the math. The agent receives raw dates and counts from tool calls and must compute the answer itself. This is intentional to train the reasoning. ### Level 3 - Open-ended technical analysis Questions that require reading patent content, understanding technical concepts, and synthesizing answers that can't be verified by pure string matching. | Question type | Example | | - | - | | Technical summary | "Summarize the key technical innovation in patent X in 2-3 sentences" | | Problem identification | "What problem does patent X solve?" | | Cross-patent comparison | "What is the key technical difference between patent X and Y?" | | Standards identification | "What wireless communication standards are referenced in patent X?" | | Claim feature extraction | "What are the key limiting features in claim 1 of patent X?" | | Abstract-claim mapping | "Which claims are most directly described in the abstract?" | **Dataset size:** 500 Q\&A pairs (LLM-generated with ground truth). **Reward:** LLM judge, normalized. **New tools:** `view_sections(patent_id)`, `read_section(patent_id, section_name)` *** ## Step 3 - Generate Verifiable Q\&A Pairs ### Levels 1 and 2 Iterate over the patent dataset, select random patents (or pairs for comparison questions), compute the answer directly from structured data, and generate the pair. Every answer is verified against the source data. For search-type questions, precompute all valid answers across the entire dataset. For example, if and question asks to identify "SSB" patents and three patents mention "SSB" in their title or abstract, all three filing dates are valid answers. Store this set, and the reward function will check against it at training time. ```python theme={null} # generate_qa.py import random from datetime import datetime def generate_date_question(patents: list[dict]) -> dict: patent = random.choice(patents) return { "question": f"When was patent {patent['patent_id']} filed?", "answer": patent["filing_date"], # e.g. "2019-03-15" "patent_ids": [patent["patent_id"]], "level": 1, "type": "filing_date", } def generate_days_to_grant(patents: list[dict]) -> dict: patent = random.choice([p for p in patents if p["grant_date"]]) filed = datetime.strptime(patent["filing_date"], "%Y-%m-%d") granted = datetime.strptime(patent["grant_date"], "%Y-%m-%d") days = (granted - filed).days return { "question": f"How many days elapsed between the filing and grant of patent {patent['patent_id']}?", "answer": str(days), "patent_ids": [patent["patent_id"]], "level": 2, "type": "days_to_grant", } ``` ### Level 3 - LLM-generated ground truth Each Level 3 entry needs a structured reference that the judge can use at training time. Use the LLM to generate three components per question: * **answer** - the reference answer in writing * **key\_points** - specific factual claims the answer must contain * **source\_quotes** - direct quotes from the patent text supporting each key point ```python theme={null} # generate_l3_qa.py import json from openai import OpenAI client = OpenAI() GENERATION_PROMPT = """You are generating ground truth for a patent analysis training dataset. Patent text: {patent_text} Question: {question} Return ONLY valid JSON with no preamble: {{ "answer": "prose answer to the question", "key_points": ["specific factual claim 1", "specific factual claim 2"], "source_quotes": ["exact quote from patent supporting point 1", "exact quote supporting point 2"] }}""" def generate_l3_ground_truth(patent_text: str, question: str) -> dict: response = client.chat.completions.create( model="model", messages=[{ "role": "user", "content": GENERATION_PROMPT.format( patent_text=patent_text, question=question ) }], temperature=0 ) return json.loads(response.choices[0].message.content) ``` ### Validate your dataset Run these checks on every generated pair before training: * Every patent ID referenced in a question exists in the dataset * Every Level 3 ground truth has at least one key point * Source quotes from Level 3 ground truth actually appear in the patent text (substring match) Manually review a stratified sample across all question types. This surfaces systematic prompt issues that automated checks miss such as questions conflating problem and solution, or rubric with contradictions, or answers with technical inaccuracies. *** ## Step 4 - Reward Design ### Levels 1 and 2 Level 1 and 2 reward designs were straightforward. ```python theme={null} # rewards.py def reward_exact(agent_answer: str, ground_truth: str) -> float: """Binary reward for single-answer questions.""" return 1.0 if normalize(agent_answer) == normalize(ground_truth) else 0.0 def reward_set(agent_answer: str, valid_set: set[str]) -> float: """For search questions with multiple valid answers.""" return 1.0 if normalize(agent_answer) in valid_set else 0.0 def normalize(s: str) -> str: return s.strip().lower().replace(",", "").replace(".", "") ``` ### Level 3 - LLM judge Getting the judge right required three iterations. Here's what failed and what ended up working. **Iteration 1 - didn't work:** Multi-dimensional weighted scoring (accuracy, completeness, reasoning, conciseness, 0-10 each). Failed because regex parsing of scores was fragile, and per-category weight tuning is a second optimization problem on top of the first. **Iteration 2 - didn't work:** Per-question custom rubrics (5 criteria x 2 points each, LLM-generated per question). More principled, but LLM-generated rubrics introduced contradictions like asking for content that would actually weaken an otherwise correct answer. **Iteration 3 - works:** Universal rubric with content-specific ground truth. The ground truth (answer, key\_points, source\_quotes) already provides all content specificity needed. | Criterion | Points | What it catches | | - | - | - | | Factually accurate relative to patent | 3 | Wrong technical details | | Free of hallucinated information | 3 | Made-up claims, features, standards | | Covers key points from ground truth | 2 | Missing important content | | Directly answers the question asked | 1 | Off-topic or evasive responses | | References specific patent content | 1 | Unsupported assertions | The judge receives the question, the agent's response, the reference answer, key points, and source quotes. It returns structured JSON, normalized to \[0, 1]: ```json theme={null} // judge_output_schema.json { "key_points": [ {"point": "SSB spatial overloading concept", "covered": true}, {"point": "Spatially separated beams", "covered": true}, {"point": "Reduced bandwidth overhead", "covered": false} ], "hallucination": false, "factual_error": false, "final_score": 7 } ``` ```python theme={null} # rewards.py def reward_l3(judge_output: dict) -> float: score = judge_output["final_score"] / 10.0 # normalize to [0, 1] if judge_output.get("hallucination"): score *= 0.2 # hallucination is catastrophic in patent context elif judge_output.get("factual_error"): score *= 0.5 return score ``` Note on the 0.2 multiplier: zeroing out hallucinated responses entirely creates a sharp gradient that can cause instability. A 0.2 multiplier still strongly penalizes hallucination while providing a small gradient signal. In a patent context, hallucinated technical claims have direct commercial and legal consequences so we err on the side of undertrained vs. positively trained on hallucinations. *** ## Step 5 - Build the Tool Environment The environment is implemented using the [verifiers](https://github.com/willccbb/verifiers) library as a `ToolEnv`. The agent receives a system prompt describing its role as a patent analyst with access to a dataset of N patents, a question, and a set of tools. ### Tool definitions ```python theme={null} # patent_env.py import chromadb from verifiers import ToolEnv class PatentEnv(ToolEnv): def __init__(self, patents: list[dict], level: int): self.corpus = {p["patent_id"]: p for p in patents} self.level = level self._init_vector_store(patents) super().__init__(tools=self._get_tools(level)) def _init_vector_store(self, patents): self.chroma = chromadb.Client() collection = self.chroma.create_collection("patents") collection.add( ids=[p["patent_id"] for p in patents], documents=[f"{p['title']} {p['abstract']}" for p in patents], ) self.collection = collection def search_patents(self, query: str) -> list[dict]: """Returns patent IDs and titles matching a semantic query.""" results = self.collection.query(query_texts=[query], n_results=5) return [ {"patent_id": id_, "title": self.corpus[id_]["title"]} for id_ in results["ids"][0] ] def get_metadata(self, patent_id: str) -> dict: """Returns title, filing_date, grant_date, claim_count.""" p = self.corpus[patent_id] return { "title": p["title"], "filing_date": p["filing_date"], "grant_date": p["grant_date"], "claim_count": p["claim_count"], } def get_abstract(self, patent_id: str) -> str: return self.corpus[patent_id]["abstract"] def view_sections(self, patent_id: str) -> list[str]: """Lists available section names in the full patent text.""" doc = self.corpus[patent_id]["full_text"] return [ line.lstrip("# ").strip() for line in doc.split("\n") if line.startswith("## ") ] def read_section(self, patent_id: str, section_name: str) -> str: """Returns the full text of a named section.""" doc = self.corpus[patent_id]["full_text"] start = doc.find(f"## {section_name}") if start == -1: return f"Section '{section_name}' not found." end = doc.find("\n## ", start + 1) return doc[start:end if end != -1 else len(doc)] def _get_tools(self, level: int): tools = [self.search_patents, self.get_metadata] if level >= 2: tools.append(self.get_abstract) if level >= 3: tools += [self.view_sections, self.read_section] return tools ``` Keep tools stateless. The agent can call `get_metadata()` on the same patent multiple times with no side effects. ### Schema to tools generalization Patents have standardized sections (Abstract, Claims, Description) and coded metadata. This makes them well-suited for tool-based environments. The same structure applies to any document domain with a known schema such as legal filings map to `get_case_metadata()`, SEC filings to `read_section("Risk Factors")`, and so on. *** ## Step 6 - Parsing Strategy Avoid regex in the pipeline because when not implemented well, it can introduce reward hacking. Use simple string methods and structured JSON instead: ```python theme={null} # parsing.py import json def extract_claims_text(full_text: str) -> str: """Extract the Claims section using string slicing, not regex.""" start = full_text.find("## Claims") if start == -1: return "" end = full_text.find("\n## ", start + 1) return full_text[start:end if end != -1 else len(full_text)] def parse_patent_id(full_code: str) -> str: """'US10123456B2' -> split and handle deterministically.""" return full_code.split("-")[0] if "-" in full_code else full_code def parse_judge_output(raw: str) -> dict: """Parse judge JSON with a robust fallback.""" try: return json.loads(raw) except json.JSONDecodeError: # Fallback: find final_score by character iteration key = '"final_score":' idx = raw.find(key) if idx == -1: return {"final_score": 0, "hallucination": True, "key_points": []} score_start = idx + len(key) digits = "" for ch in raw[score_start:].lstrip(): if ch.isdigit(): digits += ch elif digits: break return { "final_score": int(digits) if digits else 0, "hallucination": "hallucin" in raw.lower(), "key_points": [], } ``` *** ## Step 7 - Train on Prime Intellect [Hosted Training](/hosted-training/getting-started) handles GPU orchestration for you. You push your environment to the Environments Hub, define a training config, and launch the run from the CLI. ### Step 7.1 - Push environment to the Hub ```bash theme={null} # Push each level as a separate named environment prime env push --name basic-patent-q-and-a prime env push --name advanced-patent-q-and-a prime env push --name patent-technical-analysis ``` ### Step 7.2 - Define a training config **Suggested starting point:** a 3B-class instruct model, `max_steps = 100`, `batch_size = 128`, `group_size = 8`, `max_completion_tokens = 1024`, with one `[[orchestrator.train.source]]` pointing at your pushed environment. Pass `OPENAI_API_KEY` at dispatch with `-e` so the Level 3 judge can run. For the config format, see [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config). ### Step 7.3 - Calibrate model to environment difficulty Before committing to a full training run, verify the base model finds the task challenging but not impossible. A model that starts too high has nothing to learn; one that starts too low can't generate useful gradient signal. Target starting reward is around 0.15 to 0.35. Do a short 10-step run first and read the reward curve. It plots mean reward per step across all rollouts in the batch. If starting reward is outside that range, adjust difficulty or model size before launching the full run. ```bash theme={null} # Quick calibration: 10 steps to measure starting reward prime train config.toml \ --env-var OPENAI_API_KEY=sk-... \ --override max_steps=10 ``` If starting reward is above 0.7, make the questions harder or use a smaller base model. If it's below 0.1, the questions may be too hard or the tools too opaque. ### Step 7.4 - Launch the full run ```bash theme={null} prime train config.toml --env-var OPENAI_API_KEY=sk-... ``` You can pass secrets at runtime with `--env-var`, or set them in the environment settings — both work. Once the run is live, the dashboard shows per-step metrics including mean reward, reward standard deviation, response length, and tool call count. Tool call count is worth checking early. If the agent isn't calling tools in the first few steps, the system prompt isn't clear enough about what tools are available and when to use them. Below are some screenshots of what the Prime Intellect dashboard looks like. ![Reward and metrics dashboard](https://mintcdn.com/primeintellect/WdmRRTmBxbiqzYNi/guides/reward_metrics.png?fit=max\&auto=format\&n=WdmRRTmBxbiqzYNi\&q=85\&s=ed734ceaecb64f3bad4b979a460b19e0) ![Rollout viewer](https://mintcdn.com/primeintellect/WdmRRTmBxbiqzYNi/guides/image__1_.png?fit=max\&auto=format\&n=WdmRRTmBxbiqzYNi\&q=85\&s=464adcfa91f15d4c6730b5c709bfda3d) ### Config parameters to tune | Parameter | What it controls | Starting point | | - | - | - | | `max_steps` | Training duration | 50 for L1, 100 for L2/L3 | | `batch_size` | Examples per step | 128 | | `rollouts_per_example` | Trajectories sampled per question | 8, increase for noisy L3 rewards | | `max_tokens` | Max agent response length | 1024, L3 may need more | *** ## Results Trained Qwen3-4B-Instruct and Llama-3.2-3B-Instruct across all three levels. The rollout viewer lets you inspect individual trajectories turn by turn: each tool call, its response, and the final answer. It's the most direct way to see what the model is actually learning to do at each level and understand in depth why the reward curve is behaving as it is. ### Level 1 | Checkpoint | Reward | | - | - | | Start | \~0.05 | | Step 15 | \~1.0 | ![Level 1 reward curve](https://mintcdn.com/primeintellect/WdmRRTmBxbiqzYNi/guides/patent_level_1_updated.png?fit=max\&auto=format\&n=WdmRRTmBxbiqzYNi\&q=85\&s=0bac94acae279a06122ea414911c5da7) Saturates within 15 steps and stays there. Confirms the pipeline works end-to-end. Once the agent learns the pattern of calling `get_metadata()` and reading the response, it gets nearly everything right. ### Level 2 | Checkpoint | Reward | | - | - | | Start | \~0.25 | | Step 50 | \~0.70 | ![Level 2 reward curve](https://mintcdn.com/primeintellect/WdmRRTmBxbiqzYNi/guides/patent_level_2.png?fit=max\&auto=format\&n=WdmRRTmBxbiqzYNi\&q=85\&s=ddbcdf73d30999372d86b522efb4c14d) Clear upward trend with variance throughout. Some batches land on straightforward date subtraction, others require chaining a search into a metadata call and then doing a comparison. The curve hasn't plateaued at step 50, so longer runs will get more performance. ### Level 3 | Checkpoint | Reward | | - | - | | Start | \~0.30 | | Step 100 | \~0.50 | ![Level 3 reward curve](https://mintcdn.com/primeintellect/WdmRRTmBxbiqzYNi/guides/patent_level_3.png?fit=max\&auto=format\&n=WdmRRTmBxbiqzYNi\&q=85\&s=c5f043b2b6308ab3f6f2e5359e9b3c4d) Noisy, as expected from LLM-judged reward. Individual batches swing between 0.15 and 0.9. The upward trend is real but needs larger batches and more steps to converge. The noise has two sources: judge subjectivity, and question difficulty variation (ex: technical summary vs. cross-patent comparison are not the same task and vary in difficulty). *** ## Extending This to Other Domains The core architecture (schema-derived tools, progressive difficulty levels, universal rubric with content-specific ground truth) is not patent-specific. To adapt it to the following categories, you can: * **Legal case search:** Replace `read_section("Claims")` with `read_section("Holding")`. The judgment or ruling is your Level 3 answer target. * **SEC filings:** 10-K documents have standardized sections (Risk Factors, MD\&A, Financial Statements). * **Medical literature:** PubMed abstracts for Level 1, full-text PMC articles for Level 3. Use MeSH terms for structured metadata. * **Enterprise knowledge bases:** Internal docs with known schemas. Level 3 judge needs domain-appropriate ground truth generation. The main work in each new domain is: (1) acquiring the data with full text, (2) defining the question types that capture the actual analytic tasks, and (3) writing the Level 3 ground truth generation prompt with domain-specific few-shot examples. *** All environments are published on the [Environments Hub](https://hub.primeintellect.ai/). The patent dataset is on [HuggingFace](https://huggingface.co/datasets/jessicafeiyali/wirelesspatents). # Billing Source: https://docs.primeintellect.ai/hosted-training/billing How on-demand Hosted Training runs are metered and billed On-demand Hosted Training is billed **per GPU-hour**, for the GPUs your run occupies. ## Where to find rates Rates are set per GPU type and change over time, so they live in the CLI rather than in these docs: ```bash theme={null} prime train models ``` The **On-Demand Capacity** table lists each purchasable GPU type with its current `$/GPU-hour`. A GPU type with no active rate isn't sold on-demand and won't appear. When you dispatch, the CLI echoes the GPU type it resolved and the rate it will bill at, so the cost is visible before the run starts rather than first appearing on an invoice: ```bash theme={null} prime train configs/rl.toml ``` > [!NOTE] > > The rate is snapshotted onto your run at dispatch. If rates change while your run is in flight, your run keeps the rate it started with. ## What gets billed | | | | - | - | | **Meter starts** | When your pods are **Ready** | | **Meter stops** | When the run reaches a terminal state or is [paused](/hosted-training/pause-resume) | | **Granularity** | Prorated to the second, no minimum | Queueing, scheduling, and image pull are **not** billed. A cold node pulling a multi-gigabyte prime-rl image can take many minutes — that time is on us. ## Dispatch requirements A run is admitted against your wallet, so before dispatch you need: * **On-demand enabled** on your account or team. On-demand training is in beta, so [request access](mailto:contact@primeintellect.ai) if it is not enabled yet * **Spendable balance** — your balance plus any overdraft allowance and training credit grants * **A run within your per-run GPU cap** Credits and discounts are applied as grants against your wallet, and grants are consumed before your balance. ## Volume storage [Volumes](/hosted-training/volumes) are billed separately from GPU-hours: **\$0.09 per GiB per month** (\$0.000125 per GiB-hour) on the volume's size, from creation until you delete it, whether or not a run is using it. See [Volume pricing](/hosted-training/volumes#pricing). ## Capacity and queueing If the on-demand pool has no headroom, your run **queues** rather than being rejected, and is promoted automatically as capacity frees. Your wallet is re-checked at promotion. `prime train models` shows a **Capacity** column per GPU type, either available or busy. That is a coarse hint about pool headroom, not an admission decision: whether your run starts immediately or queues also depends on how many GPUs you ask for and their topology, which the discovery endpoint does not know. > [!NOTE] > > If you already hold reserved capacity for a matching GPU type, your run is placed there instead of on-demand — that hardware is already paid for. > **[Models](/hosted-training/models)** > > Which models and GPU types you can dispatch on. > **[Hosted RL](/hosted-training/hosted-rl)** > > Config schema, dispatch knobs, and run lifecycle. # End-to-End Training Run Source: https://docs.primeintellect.ai/hosted-training/end-to-end-run Walk through a complete on-demand Hosted Training run from environment setup to results This guide walks you through a complete Hosted Training run — choosing an environment, establishing a baseline, writing a config, dispatching the run, monitoring it, and reviewing results. For the config schema and dispatch options in reference form, see [Hosted RL](/hosted-training/hosted-rl). ## Prerequisites ```bash theme={null} # Install and authenticate the CLI uv tool install -U prime prime login ``` You also need on-demand training enabled and a funded wallet — see [Billing](/hosted-training/billing). Start from [Getting Started](/hosted-training/getting-started) if you haven't run anything yet. Create a volume to hold the trained weights. Checkpointing is off by default and pods are torn down when a run ends, so without a volume and a `[ckpt]` block the run leaves you nothing to evaluate: ```bash theme={null} prime volumes create research --size 100Gi ``` ## Step 1: Choose an Environment You can use an existing environment from the [Environments Hub](https://app.primeintellect.ai/dashboard/environments) or create your own. For this walkthrough we'll use `alphabet-sort` — a multi-turn game where the model must sort letters into alphabetical order. If you're new to how environments work, see [The Environment Model](/hosted-training/environment-model). ## Step 2: Run a Baseline Evaluation Before training, evaluate the base model to establish a baseline. This confirms the environment works and shows you where the model starts: ```bash theme={null} prime eval run primeintellect/alphabet-sort \ -m Qwen/Qwen3.5-2B \ -n 20 -r 1 --hosted ``` > [!TIP] > > A good training environment should have a baseline reward between roughly 10–80%. If the model scores 0% after many attempts, the task is too hard. If it's already at 80%+, consider harder examples or a different environment. The evaluation runs on the platform, so there's nothing to install locally. The command prints an evaluation ID and a dashboard link where the results appear. To stream its logs: ```bash theme={null} prime eval logs -f ``` See [Evaluating environments](/tutorials-environments/evaluating) for the full set of hosted evaluation options. ## Step 3: Choose a Model and GPU Type ```bash theme={null} prime train models ``` This lists the models you can dispatch, plus an **On-Demand Capacity** table of purchasable GPU types with their current `$/GPU-hour` and whether a run starts now or queues. Runs boot from a cluster-side weight cache, so pick a model from that list — see [Models](/hosted-training/models). For a first run, pick the smallest model that exercises your environment end to end. Validate cheaply, then scale. ## Step 4: Create a Training Config Training runs are configured with a [prime-rl](/prime-rl/configuration) `.toml` file. The `[deployment]` block sizes the run and is what routes it onto Hosted Training. ```toml theme={null} # configs/alphabet-sort.toml name = "alphabet-sort-rl" max_steps = 50 seq_len = 2048 [ckpt] # save step 50 to the volume interval = 50 keep_last = 1 [model] name = "Qwen/Qwen3-4B-Instruct-2507" # cached models in `prime train models` start warm [deployment] num_train_gpus = 1 num_infer_gpus = 1 [trainer.optim] lr = 1e-6 [orchestrator] batch_size = 128 [orchestrator.train] group_size = 8 [orchestrator.train.sampling] max_completion_tokens = 512 [[orchestrator.train.source]] name = "alphabet-sort" [orchestrator.train.source.env.taskset] id = "alphabet-sort" [orchestrator.train.source.env.agent.harness] id = "null" [orchestrator.train.source.env.agent.runtime] type = "subprocess" [inference] ``` The key fields: * `[model].name` — a Hugging Face model ID or an absolute `/volume/...` path to [weights on your volume](/hosted-training/volume-models). Cached IDs start warm; uncached IDs download at startup on on-demand capacity * `[ckpt]` — save checkpoints. Off by default; with no `[ckpt]` block nothing is written * `[deployment]` — how many trainer and inference GPUs to reserve; required for dispatch * `max_steps` — total training steps * `[orchestrator].batch_size` — rollouts consumed per training batch * `[orchestrator.train].group_size` — rollouts generated per dataset example * `[orchestrator.train.sampling].max_completion_tokens` — max tokens the model generates per turn * `[[orchestrator.train.source]]` — one block per training environment, carrying a nested `env` block This config uses the native `alphabet-sort` taskset bundled with the hosted runtime and the `null` harness. The Hub package used for the baseline evaluation is a separate package; its legacy API is incompatible with the current hosted runtime. For other environments, use a runtime-compatible taskset and harness. ## Trace storage and costs Traces uploaded by platform runs incur [Prime Traces ingestion and storage charges](/traces/pricing-and-settings), separately from training charges. Review workspace upload controls before launching. See [Viewing and deleting traces](/traces/managing-traces) to manage the resulting data. ## Step 5: Launch the Training Run ```bash theme={null} prime train configs/alphabet-sort.toml --volume research ``` The CLI confirms the configuration, echoes the GPU type and hourly rate it resolved, and prints a dashboard link: ``` Dispatched hosted run wn2cjdrzdo6bmfqajoeuu30p Monitor run at: https://app.primeintellect.ai/dashboard/training/wn2cjdrzdo6bmfqajoeuu30p ``` Pass credentials your environment needs with `-e / --env-var` or `--env-file`: ```bash theme={null} prime train configs/alphabet-sort.toml -e WANDB_API_KEY -e OPENAI_API_KEY ``` Config errors — bad schema, an uncached model, an oversized deployment — fail the dispatch immediately with a reason, before any GPU is reserved and before anything bills. ## Step 6: Monitor the Run **In the terminal** — stream logs in real time: ```bash theme={null} prime train logs -f ``` A run has several components; pick one with `-c / --component`: ```bash theme={null} prime train logs # orchestrator (default) prime train logs -c trainer # trainer (FSDP / torchrun) prime train logs -c inference # vLLM inference server prime train logs --env # env-server for a specific env ``` Filter with `--search`, `--regex`, `--level`, and `--since`. **On the dashboard** — open the URL printed when the run started, for reward curves, rubric scores, reward distributions, and individual rollouts. Key metrics to watch: * **Reward** — the overall reward curve should trend upward over time * **Rubric** — individual rubric component scores * **Reward Distribution** — should shift from lower to higher values as training progresses Stop a run early to release its GPUs and stop the meter: ```bash theme={null} prime train stop ``` To release the GPUs but continue from the current step later, [pause](/hosted-training/pause-resume) the run instead. ## Step 7: Review Results ### Retrieve the checkpoint The RL run's checkpoints, logs, and other outputs are saved to the [volume](/hosted-training/volumes) when the run ends. Wait for the volume sync to finish, then download the run directory: ```bash theme={null} prime volumes get research runs// ./run-dir ``` ### Export to HF format Trainer checkpoints are DCP-sharded, so they need converting before anything else can load them. The converter is `tools/convert_dcp_to_bf16.py` in the [prime-rl](https://github.com/PrimeIntellect-ai/prime-rl) repo. It needs: * **At least one GPU.** * **The run's resolved config.** The converter reads model and tokenizer settings from `/configs/latest/resolved/trainer.json` for RL or `sft.json` for SFT. Hosted runs currently do not save this config with the checkpoints. Obtain it from Prime support and place it at the required path before converting. The launch TOML is not a substitute for the resolved JSON config. * **Access to the base model and tokenizer.** If the resolved config uses `/volume/...` paths, make those directories available on the machine doing the conversion. Run the converter from a prime-rl checkout on a machine with a GPU: ```bash theme={null} uv run python tools/convert_dcp_to_bf16.py ./run-dir/checkpoints/step_50 # multi-rank, for faster gathers and models too big for one GPU uv run torchrun --nproc-per-node 8 tools/convert_dcp_to_bf16.py ./run-dir/checkpoints/step_50 ``` This writes sharded safetensors plus config and tokenizer assets to `./run-dir/checkpoints/step_50/weights`. That directory loads into an HF consumer. See [Exporting Checkpoints](/prime-rl/training#exporting-checkpoints) for FP8 variants. > [!NOTE] > > Hosted SFT runs save checkpoints rather than the whole run directory. Arrange the downloaded checkpoint under `run-dir/checkpoints/step_` and obtain the resolved config before running the converter. ### Evaluate against your baseline Serve the exported weights, then point the Step 2 evaluation at them. This evaluation runs locally, so install the environment first: ```bash theme={null} # install the environment locally prime env install primeintellect/alphabet-sort # serve the exported directory uv run inference --vllm.model /checkpoints/step_/weights # evaluate the same environment, same sample size as Step 2 uv run vf-eval primeintellect/alphabet-sort \ -m /checkpoints/step_/weights \ -b http://localhost:8000/v1 -k OPENAI_API_KEY \ -n 20 -r 1 -s ``` `-b` points `vf-eval` at your endpoint and `-k` names the env var holding its key. Compare the reward against the baseline you recorded in Step 2. > [!NOTE] > > Metrics, samples, and progress are persisted to the dashboard either way, but trainer, inference, and env-server pod logs go away with the pods when the run is torn down — pull anything you need while the run is live. ## Run Size Guidelines Each of these omits the blocks already covered above. Add `[ckpt]` and dispatch with `--volume` to keep weights. See [Checkpoint settings](/hosted-training/volumes#checkpoint-settings) for retention settings. ### Small (validation) Verify your environment and config work before committing to a longer run. ```toml theme={null} max_steps = 50 seq_len = 2048 [deployment] num_train_gpus = 1 num_infer_gpus = 1 [orchestrator] batch_size = 128 [orchestrator.train] group_size = 8 ``` ### Medium (experimentation) Good for iterating on environment design and hyperparameters. ```toml theme={null} max_steps = 200 seq_len = 4096 [deployment] num_train_gpus = 2 num_infer_gpus = 2 [orchestrator] batch_size = 256 [orchestrator.train] group_size = 16 [wandb] project = "my-experiment" ``` ### Large (production) ```toml theme={null} max_steps = 500 seq_len = 32768 [deployment] type = "multi_node" num_train_nodes = 2 num_infer_nodes = 2 [trainer.model] attn = "flash_attention_3" [orchestrator] batch_size = 512 [orchestrator.train] group_size = 16 [wandb] project = "production" ``` > [!NOTE] > > `WANDB_API_KEY` is required for the `[wandb]` block to take effect — without it the block is stripped and the run trains without W\&B logging. > **[Hosted RL](/hosted-training/hosted-rl)** > > Config reference, dispatch knobs, limits, and run lifecycle. > **[Billing](/hosted-training/billing)** > > How GPU-hours are metered and what gets billed. > **[Troubleshooting](/hosted-training/troubleshooting)** > > Solutions for common issues with Hosted Training runs. # The Environment Model Source: https://docs.primeintellect.ai/hosted-training/environment-model Understand environment types, rubrics, and the RL training loop ## The Training Loop When you launch an RL training run, three decoupled components coordinate: * **Inference** generates model completions turn-by-turn via a vLLM server. * **Orchestrator** samples prompts from the environment's dataset, dispatches rollouts to inference, drives the multi-turn interaction loop (calling the environment's response logic and tool execution between each model turn), scores completed rollouts via the rubric, packs training batches, and relays weight updates between trainer and inference. * **Trainer** receives scored rollouts, computes advantages, and produces weight updates via GRPO. One step of training: ``` Orchestrator samples prompts from the environment -> Inference generates a rollout (multi-turn, driven by env logic) -> Environment scores the rollout via its rubric -> Orchestrator packs the batch and computes advantages -> Trainer updates policy weights -> Orchestrator broadcasts new weights to inference -> Repeat ``` The full loop is asynchronous. Rollout generation at step N overlaps with training at step N-1, and a single trajectory may span multiple policy versions as weights update mid-rollout. **The environment is the only part you write.** Everything else (batching, weight sync, GPU scheduling, async rollout management) is handled by the infrastructure. An environment plugs directly into this loop, whether you run it via Hosted Training or self-hosted with `prime-rl`. *** ## What is an Environment? An environment is a self-contained Python module that packages three things: 1. **A dataset** of prompts (with optional ground-truth answers or metadata). 2. **A harness** that controls how the model interacts with the task: single-turn Q\&A, multi-turn tool calling, stateful sandbox sessions, etc. 3. **A rubric** of reward functions that score the model's output and produce scalar signals. The same environment definition drives RL training, standalone evaluation (`vf-eval` locally, or `prime eval run --hosted`), and synthetic data generation. RL environments and agent evals are the same abstraction: dataset + harness + scoring rules. Environments are distributed as versioned Python wheels declared in `pyproject.toml`, installable and shareable via the Environments Hub. ### Legacy and Verifiers v1 environment configs Verifiers ships two packaging/configuration styles, and Hosted Training supports both: * **Legacy (Verifiers v0) environments** expose a `load_environment(**args)` entrypoint. * **Verifiers v1 (taskset/harness) environments** split task data and scoring from model-facing execution. In training TOML, these use `env.taskset = { id = "..." }` and optional `env.agent.harness = { id = "..." }` inside a training source. In the Verifiers v1 model, the **taskset** owns train/eval tasks, task controls, rewards, metrics, and task-owned tools. The **harness** is the program that drives the model — the built-in `default` harness, or an agent harness such as `bash` or `codex`. Its `runtime` field selects *where* that program runs: a local `subprocess`, or a `docker`/`prime`/`modal` sandbox. The Verifiers v1 `Environment` composes a taskset and harness and plugs into the same evaluation and training loop as legacy environments (the `vf.Environment` subclasses such as `SingleTurnEnv` and `ToolEnv` described below). Hosted Training uses the native `prime-rl` config schema: training environments are declared as an array of `[[orchestrator.train.source]]` tables, each carrying a nested `env` block. See [End-to-End Run](/hosted-training/end-to-end-run#step-4-create-a-training-config) for a worked example. *** ## The Type Hierarchy All user-facing environment types descend from `Environment`, the abstract base class that defines the dataset, rubric, and rollout interface. `MultiTurnEnv` extends `Environment` and implements the core rollout loop — the `rollout()` method is finalized there. The concrete types (`SingleTurnEnv`, `ToolEnv`, `StatefulToolEnv`) each extend `MultiTurnEnv`, layering interaction complexity progressively. ### `SingleTurnEnv` Prompt in, completion out, reward computed. This is `MultiTurnEnv` with `max_turns=1`. Use it for standard Q\&A benchmarks. ```python theme={null} import verifiers as vf dataset = vf.load_example_dataset("gsm8k") async def correct_answer(completion, answer) -> float: return 1.0 if completion[-1]["content"] == answer else 0.0 env = vf.SingleTurnEnv( dataset=dataset, rubric=vf.Rubric(funcs=[correct_answer]), ) ``` ### `MultiTurnEnv` The base class for anything conversational or interactive. Override two hooks: * **`env_response(messages, state)`**: produce the environment's next message given the conversation so far. Game logic, simulation steps, or external calls go here. * **`@vf.stop` methods**: async methods decorated with `@vf.stop` act as termination conditions. The rollout ends when any stop condition returns `True` or `max_turns` is reached. The rollout loop alternates between model inference and `env_response` until a stop condition fires or `max_turns` is reached. ### `ToolEnv` Adds native tool/function-calling. Pass a list of Python functions; `verifiers` extracts JSON schemas from type hints and docstrings, wires them into the OpenAI-compatible tool-calling protocol, and dispatches calls during rollouts. ```python theme={null} async def calculate(expression: str) -> str: """Evaluate a mathematical expression. Args: expression: A math expression to evaluate (e.g. "2 + 2 * 3") """ return str(eval(expression)) env = vf.ToolEnv( dataset=dataset, tools=[calculate, search_tool], rubric=rubric, max_turns=10, ) ``` Tools in `ToolEnv` must be **stateless and idempotent**: each call is fully determined by its arguments. The environment terminates when the model responds without issuing any tool calls (or hits `max_turns`). ### `StatefulToolEnv` When tools need per-rollout state (a sandbox handle, a database connection, a session token), `StatefulToolEnv` adds two hooks: * **`setup_state(state)`**: called at rollout start to initialize a fresh state dict. * **`update_tool_args(tool_name, args, messages, state)`**: intercepts each tool call to inject state into arguments before dispatch. All mutable state lives in the state dict. Never store globals. *** ## Environment Structure ``` my_env/ ├── my_env.py # Implementation ├── pyproject.toml # Dependencies + metadata └── README.md # Documentation ``` The entry point is `load_environment()`, which returns an `Environment` instance. This is what the training and evaluation infrastructure calls. ```python theme={null} def load_environment(split: str = "train") -> vf.Environment: dataset = load_dataset("my-org/my-dataset", split=split) rubric = vf.Rubric( funcs=[correctness, format_score], weights=[0.8, 0.2], ) return vf.ToolEnv( dataset=dataset, tools=[search_tool, execute_tool], rubric=rubric, max_turns=5, ) ``` Expensive setup (loading datasets, building indices) goes in `load_environment()` or `__init__()`. Per-rollout setup goes in `setup_state()`. *** ## Rubrics A `Rubric` is a set of scoring functions evaluated after each rollout. Functions can be sync or async, and receive keyword arguments like `prompt`, `completion`, `answer`, `state`, and `parser` depending on what they need. Functions are weighted and combined into a scalar reward. You can also define non-reward metrics (token count, tool call frequency) that get logged without affecting gradients. For subjective criteria, `JudgeRubric` delegates scoring to an external LLM judge. # Getting Started Source: https://docs.primeintellect.ai/hosted-training/getting-started Launch your first on-demand Hosted Training run Train a model with reinforcement learning on Prime Intellect's infrastructure — no GPUs to manage, no cluster to reserve. > [!NOTE] > > On-demand training is in **beta**. [Request access](mailto:contact@primeintellect.ai) before your first run, or dispatch will be refused. ## Pick your path | | What it is | Access | | - | - | - | | **[Hosted Training](#1-install-the-cli-and-log-in)** | You submit a config, we run it on GPUs we operate, billed [per GPU-hour](/hosted-training/billing). | Beta, [request access](mailto:contact@primeintellect.ai) | | **[Model Factory Clusters](/model-factory/clusters)** | A dedicated cluster running training, inference, Slurm and Kubernetes side by side. Use this when you need control over the stack. | [Contact us](mailto:contact@primeintellect.ai) | | **[prime-rl](/prime-rl/overview)** | The open-source training framework, running on your own hardware. | Self-hosted | Hosted Training is the fastest way to start, and the rest of this page covers it. ## Prerequisites * Python 3.10+ * A [Prime Intellect account](https://app.primeintellect.ai) with on-demand training enabled and a funded wallet, see [Billing](/hosted-training/billing) ## 1. Install the CLI and log in ```bash theme={null} # install uv (if not already installed) curl -LsSf https://astral.sh/uv/install.sh | sh # install the prime CLI uv tool install -U prime # log in prime login ``` ## 2. See what you can run ```bash theme={null} prime train models ``` This lists the models available to dispatch and an **On-Demand Capacity** table of purchasable GPU types with their current `$/GPU-hour`. Models on that list are already cached cluster-side and start warm. An on-demand run can still train a model that is not listed, it just downloads the weights first. ## 3. Create a volume to keep your weights Checkpointing is **off by default**, and when a run finishes its pods are torn down. Without a volume and a `[ckpt]` block, a completed run leaves you with dashboard metrics and no model. ```bash theme={null} prime volumes create research --size 100Gi ``` Add `[ckpt]` to save weights. The example below also limits recent checkpoints; see [Volumes](/hosted-training/volumes#checkpoint-settings) for retention, sizing, and cluster-specific requirements. ## Trace storage Before launching, review [Prime Traces pricing and upload settings](/traces/pricing-and-settings). Traces uploaded by platform runs incur ingestion and storage charges. Stored traces can be explored in the [Traces dashboard](https://app.primeintellect.ai/dashboard/traces). The November 6 [legacy sample storage retirement](/traces/legacy-storage) is separate from the [retirement of shared LoRA training](/hosted-training/shared-lora-retired). ## 4. Launch a run Training runs are configured with a [prime-rl](/prime-rl/configuration) `.toml` file. The `[deployment]` block sizes the run and is what routes it onto Hosted Training. Save this as `configs/rl.toml` — one trainer GPU and one inference GPU: ```toml theme={null} name = "reverse-text-hosted" max_steps = 100 seq_len = 2048 [ckpt] # save step 100 to the volume interval = 100 keep_last = 1 [model] name = "PrimeIntellect/Qwen3-0.6B-Reverse-Text-SFT" [deployment] num_train_gpus = 1 num_infer_gpus = 1 [trainer.optim] lr = 3e-6 [orchestrator] batch_size = 64 [orchestrator.train] group_size = 8 [orchestrator.train.sampling] max_completion_tokens = 512 [[orchestrator.train.source]] name = "reverse-text" [orchestrator.train.source.env.taskset] id = "reverse-text" [orchestrator.train.source.env.agent.harness] id = "null" [orchestrator.train.source.env.agent.runtime] type = "subprocess" [orchestrator.renderer] name = "prime-qwen3" [inference] ``` Dispatch it against the volume: ```bash theme={null} prime train configs/rl.toml --volume research ``` The CLI echoes the GPU type and hourly rate it resolved, then prints a run ID and dashboard link. ## 5. Monitor progress **Terminal:** ```bash theme={null} prime train logs -f ``` **Dashboard:** open the link from the previous step for reward curves, rubric scores, and individual rollouts in real time. Stop a run early to release its GPUs and stop the meter: ```bash theme={null} prime train stop ``` ## 6. Get your weights Wait for the run and its final volume sync to finish, then download its checkpoints: ```bash theme={null} prime volumes get research runs//checkpoints/ ./checkpoints ``` Checkpoints are DCP-sharded and need converting before anything else can load them — see [Export to HF format](/hosted-training/end-to-end-run#export-to-hf-format). ## Next steps > **[End-to-End Run](/hosted-training/end-to-end-run)** > > Full walkthrough with baseline evals, model selection, and result review. > **[Hosted RL](/hosted-training/hosted-rl)** > > Config reference, dispatch knobs, and run lifecycle. > **[Billing](/hosted-training/billing)** > > How runs are metered and what gets billed. > **[Platform Overview](/platform-overview)** > > How environments, training, evaluation, and inference fit together. # Hosted RL (Beta) Source: https://docs.primeintellect.ai/hosted-training/hosted-rl On-demand full-parameter RL training on dedicated clusters Hosted Training updates every parameter of the model. Your run gets its own trainer, inference, and orchestrator pods, sized by the `[deployment]` block in your config. Runs dispatch **on-demand** — no reserved cluster required — and are [billed per GPU-hour](/hosted-training/billing) for as long as the pods are running. ## Before you start Models listed by `prime train models` are cached and start warm. On-demand runs can also download uncached Hugging Face models at startup, which adds billable time. Runs on a reserved cluster need the base model cached there, unless they load it from a volume. Both RL and Hosted SFT support `/volume/...` model paths with `--volume`; see [Volume-hosted models](/hosted-training/volume-models). ```bash theme={null} prime train models ``` This lists the models you can dispatch, plus an **On-Demand Capacity** table of purchasable GPU types with their current `$/GPU-hour`. If the model you want is missing, [reach out](mailto:contact@primeintellect.ai) to get it added. To see every GPU type you can dispatch on: ```bash theme={null} prime train gpus ``` Dispatch is admitted against your wallet, so you also need on-demand enabled, a spendable balance, and a run that fits your per-run GPU cap — see [Billing](/hosted-training/billing). Two platform limits apply by default: | Limit | Default | | - | - | | GPUs per run (trainer + inference, all pods) | 64, or your per-run GPU cap if lower | | Queued runs per user | 10 | Reach out if your campaign needs more than that. ## Config Hosted Training runs use the native [prime-rl](https://github.com/PrimeIntellect-ai/prime-rl) config schema unchanged — no platform-specific fields. Size the run with a `[deployment]` block (see [Launching a run](#launching-a-run)). Minimal single-node example (1 trainer GPU + 1 inference GPU): ```toml theme={null} name = "reverse-text-hosted" max_steps = 100 seq_len = 2048 [model] name = "PrimeIntellect/Qwen3-0.6B-Reverse-Text-SFT" [deployment] num_train_gpus = 1 num_infer_gpus = 1 [trainer.optim] lr = 3e-6 [orchestrator] batch_size = 64 [orchestrator.train] group_size = 8 [orchestrator.train.sampling] max_completion_tokens = 512 [[orchestrator.train.source]] name = "reverse-text" [orchestrator.train.source.env.taskset] id = "reverse-text" [orchestrator.train.source.env.agent.harness] id = "null" [orchestrator.train.source.env.agent.runtime] type = "subprocess" # This fine-tune's name misses the renderer map's exact match, so select explicitly. [orchestrator.renderer] name = "prime-qwen3" [inference] ``` Multi-node example (2 train nodes + 2 inference nodes, each a full 8-GPU node): ```toml theme={null} name = "qwen30b-math" seq_len = 32768 [model] name = "Qwen/Qwen3-30B-A3B-Thinking-2507" [deployment] type = "multi_node" # required — `single_node` is the default variant num_train_nodes = 2 num_infer_nodes = 2 # per inference replica; see `num_infer_replicas` [trainer.model] attn = "flash_attention_3" ep = 8 # expert parallel (MoE) [trainer.optim] type = "adamw" lr = 1e-6 [orchestrator] batch_size = 512 max_off_policy_steps = 8 [orchestrator.concurrency] max_inflight = 1024 # ceiling on in-flight episodes [orchestrator.train.sampling] max_completion_tokens = 32768 [[orchestrator.train.source]] name = "math" [orchestrator.train.source.env.taskset] id = "i3_math" [orchestrator.train.source.env.agent.harness] id = "null" [orchestrator.train.source.env.agent.runtime] type = "subprocess" [inference.vllm] tensor_parallel_size = 8 # tensor parallel inside each inference replica ``` > [!NOTE] > > Training and eval environments are arrays of `source` tables, each carrying a nested `env` block (`env.taskset`, `env.agent.harness`, `env.agent.runtime`). The older flat `[[orchestrator.train.env]]` shape with an `id` field was removed from prime-rl and is rejected at dispatch — see [Environments](/prime-rl/configuration#environments) for the current shape. > [!TIP] > > Multi-node runs broadcast weights over NCCL by default and auto-discover the cluster's RDMA devices — no extra config needed. See the [prime-rl docs](/prime-rl/configuration) and [config examples](https://github.com/PrimeIntellect-ai/prime-rl/tree/main/configs) for the full schema. ## Launching a run Every RL config dispatches as a Hosted Training run — no flag needed. A `[deployment]` block sizes the run; without one, prime-rl's own defaults apply, same as a local run: ```bash theme={null} prime train configs/rl.toml ``` On dispatch you get a run ID and a dashboard link: ``` Dispatched hosted run wn2cjdrzdo6bmfqajoeuu30p Monitor run at: https://app.primeintellect.ai/dashboard/training/wn2cjdrzdo6bmfqajoeuu30p ``` Pass credentials with `-e / --env-var` or `--env-file`: ```bash theme={null} prime train configs/rl.toml -e WANDB_API_KEY -e HF_TOKEN -e OPENAI_API_KEY ``` `-e KEY` reads the value from your shell, `-e KEY=VALUE` sets it inline, and `--env-file path/to/.env` loads a whole file. Every key you pass is written to a per-run secret and projected onto the trainer, inference, orchestrator, and env-server pods, so an environment that needs a third-party API key can read it straight from the process environment. `WANDB_API_KEY` and `HF_TOKEN` are recognized and routed to their own fields. A few constraints on the secret map: | Constraint | Value | | - | - | | Entries per run | 32 | | Name | POSIX env-var name, ≤ 128 chars | | Value | ≤ 32 KB | | Total | ≤ 256 KB | > [!NOTE] > > `WANDB_API_KEY` is required for the `[wandb]` block to take effect — without it the block is stripped and the run trains without W\&B logging. Two dispatch-level knobs control the dispatch. Both accept a CLI flag or a top-level TOML key, and the flag wins: | Knob | Flag | TOML | Default | | - | - | - | - | | prime-rl image build | `--image-tag v0.5.1` | `image_tag = "v0.5.1"` | `main` | | GPU type to dispatch on | `--gpu-type H200_141GB` | `gpu_type = "H200_141GB"` | auto-pick | Checkpointing is **off by default**. To keep model weights, add a `[ckpt]` block, create a [volume](/hosted-training/volumes), and pass its name with `--volume`. Checkpoints live at `runs//checkpoints/`. The run directory is saved to cloud storage when the run ends; `keep_interval` and `keep_last` are optional retention settings. See [Checkpoint settings](/hosted-training/volumes#checkpoint-settings). ## Run lifecycle A dispatched run moves through: ``` QUEUED → PENDING → CREATING → PULLING_IMAGE → RUNNING → COMPLETED / FAILED / STOPPED ``` * **QUEUED** — no cluster has GPU headroom yet. The run is admitted and promoted automatically as capacity frees; you don't need to resubmit. * **PULLING\_IMAGE** — pods are scheduled and pulling the prime-rl image. This is multiple GB and can take many minutes on a cold node. It's expected, not a stall. Config errors (bad schema, uncached model, oversized deployment) are caught synchronously and fail the dispatch immediately with a reason, before any GPU is reserved. Stop a run early to release the cluster: ```bash theme={null} prime train stop ``` To release the cluster but continue from the current step later, [pause](/hosted-training/pause-resume) the run instead. > [!WARNING] > > When a run reaches a terminal state, its cluster resources are torn down automatically. Orchestrator logs are captured to the run record first, but trainer, inference, and env-server pod logs go away with the pods — pull anything you need while the run is live. Metrics, samples, distributions, and progress are persisted and stay available in the dashboard. Trace uploads have separate [pricing and workspace controls](/traces/pricing-and-settings); see [Managing traces](/traces/managing-traces) for viewing, downloading, and deleting them. Older legacy samples are subject to the [November 6 storage retirement](/traces/legacy-storage). ## Monitoring A Hosted Training run has several distinct components. Pick which one to read with `-c / --component`: ```bash theme={null} prime train logs # orchestrator (default) prime train logs -c trainer # trainer (FSDP / torchrun) prime train logs -c inference # vLLM inference server prime train logs --env # env-server for a specific env ``` List the orchestrator and env-server components for a run: ```bash theme={null} prime train components ``` Follow and filter with `-f`, `--search`, `--regex`, `--level`, and `--since`. See [Monitoring](/hosted-training/end-to-end-run#step-6-monitor-the-run) for details. The dashboard shows reward curves, rubric scores, and individual rollouts at `https://app.primeintellect.ai/dashboard/training/`. > **[End-to-End Run](/hosted-training/end-to-end-run)** > > Step-by-step walkthrough of a complete run, from baseline eval to results. > **[prime-rl Configuration](/prime-rl/configuration)** > > Full reference for the underlying training framework config schema. # Hosted SFT (Beta) Source: https://docs.primeintellect.ai/hosted-training/hosted-sft Supervised fine-tuning on Hosted Training: volumes, dataset uploads over SSH, and prime train dispatch > [!NOTE] > > Hosted SFT is in **beta**. Contact Prime support if dispatch is refused. Run `prime-rl` supervised fine-tuning on Prime's hosted GPU clusters: create a volume, copy your dataset onto it over SSH, dispatch. No `kubectl`, no cluster credentials — the trainer reads the dataset from the volume. This walkthrough fine-tunes a small Qwen3 model to reverse text ([`reverse-text`](https://github.com/PrimeIntellect-ai/prime-rl/tree/main/examples/basic/reverse-text)) on one GPU. For SFT on your own infrastructure, see [Training](/prime-rl/training). ## Prerequisites ```bash theme={null} uv tool install prime prime login ``` **Requires `prime` CLI v0.9.0 or later.** * A Prime account with hosted training access — Prime-side enablement; the platform deployment needs the hosted SFT path switched on. * A base model the run can boot from: **in the model cache** (check with `prime train models`), or **weights already on the volume itself** via a `/volume/...` `[model].name` path ([Volume-hosted models](/hosted-training/volume-models)). ```bash theme={null} prime train models ``` * A Hugging Face dataset you can download with `hf download` — with `prompt`/`completion` or `messages` columns ([Training § Dataset Format](/prime-rl/training#dataset-format)). ## 1. Create a volume ```bash theme={null} prime volumes create research --size 100Gi ``` > [!NOTE] > > Size the volume for your dataset plus the checkpoints the run keeps on its disk. A checkpoint is about 11–12 bytes per parameter: the 0.6B smoke run writes \~7.2 GB. See [Choose a size](/hosted-training/volumes#choose-a-size). Grow it with `prime volumes expand --size `. ## 2. Put the dataset on the volume Open a shell on the volume and download the dataset onto it. The download uses the cluster's network, not your laptop's. Shells need [a cluster allocation or a cluster your team owns](/hosted-training/volumes#what-you-can-do-with-and-without-a-cluster-allocation) and your [primary SSH key](/hosted-training/volumes#set-up-your-ssh-key): ```bash theme={null} prime volumes ssh research --read-write ``` Inside the session (run `hf auth login` first for private datasets): ```bash theme={null} hf download willcb/R1-reverse-wikipedia-paragraphs-v1-1000 \ --repo-type dataset \ --local-dir /volume/datasets/reverse-text exit ``` Back in your local terminal, stop the session using the ID printed when it opened: ```bash theme={null} prime volumes stop research ``` Wait for its final sync before dispatching. Exiting SSH alone leaves the read-write session open and blocks the training run. The trainer sees the volume at `/volume`, the same path you see in the shell. Without one, download the dataset to your machine and upload it instead: `prime volumes put ./my-data datasets/` creates `datasets/my-data` (paths start at the volume root, and a trailing `/` means "into this folder"). In the config, `data.name` accepts either form: a path relative to the volume (`datasets/my-data`) or the absolute mount path (`/volume/datasets/my-data`) — the platform resolves both to the same location. ## 3. Write the config Save this as `sft.toml` — `data.name` points at the dataset on the volume (relative to the volume root; absolute `/volume/...` paths work too): ```toml theme={null} max_steps = 10 [ckpt] # save step 10 to the volume interval = 10 keep_last = 1 [model] name = "PrimeIntellect/Qwen3-0.6B-Reverse-Text-SFT" # must be in the cluster model cache [data] name = "datasets/reverse-text" # volume-relative; the platform resolves it to /volume/datasets/reverse-text type = "sft" seq_len = 2048 # add splits = ["train"] for multi-split datasets batch_size = 8 [optim] lr = 2e-5 [deployment] type = "single_node" num_train_gpus = 1 gpus_per_node = 1 [renderer] name = "prime-qwen3" # must match the model family ``` > [!NOTE] > > `output_dir` is injected by the platform — omit it. The walkthrough model is a cached debug sibling already fine-tuned on this dataset — treat the loss curve as a smoke test of the hosted path. > > This walkthrough is trainer-only. To score checkpoints against an environment while the run trains, see [Online evals](#online-evals) below. Instead of a cached model ID, `[model].name` can point at HF-format weights already on the volume, uploaded with `prime volumes put`. RL configs support volume model paths too. Run checkpoints are in prime-rl's distributed format, so export them to HF format before using them as `[model].name` — or resume the full training state by pointing `[resume].dir` at the checkpoint's step directory on the volume (`dir = "/volume/runs//checkpoints/step_"`). See [Volume-hosted models](/hosted-training/volume-models#continue-from-a-previous-runs-checkpoint). ## 4. Dispatch ```bash theme={null} prime train sft.toml --volume research ``` ```text theme={null} Creating Hosted Training run... Dispatched hosted run kqeggj5dl7k85bc0mk1i4y5i Monitor run at: https://app.primeintellect.ai/dashboard/training/kqeggj5dl7k85bc0mk1i4y5i ``` Use **the run ID your dispatch printed** (``) below. The run moves `PENDING` → `CREATING` → `RUNNING` → `COMPLETED` — expect roughly three minutes on one H200 for this walkthrough. ## 5. Watch it run ```bash theme={null} prime train get prime train metrics # the loss series the dashboard renders prime train logs # the live trainer log ``` ```text theme={null} Run kqeggj5dl7k85bc0mk1i4y5i Status: COMPLETED Model: PrimeIntellect/Qwen3-0.6B-Reverse-Text-SFT Max Steps: 10 Created: 2026-09-28 20:33 Started: 2026-09-28 20:34 Completed: 2026-09-28 20:37 ``` ```text theme={null} step: [1, 2, 3, 4, 5, 6, 7, 8, 9] loss/mean: [1.108, 1.1788, 0.9996, 0.9468, 1.1179, 1.044, 1.0353, 1.0038, 0.9944] loss/perplexity: [3.028, 3.251, 2.717, 2.577, 3.058, 2.841, 2.816, 2.729, 2.703] ``` ```text theme={null} 20:35:38 [INFO] Initializing scheduler with 10 steps (type='constant') Generating train split: 100%|████████████████████| 1000/1000 [00:00<00:00, 23213.48 examples/s] 20:35:47 [SUCCESS] Step 1 | 8.5s | Loss 1.1080 | Grad. Norm 12.9495 | LR 2.00e-05 | Throughput 0 tokens/s | MFU 0.0% | Peak Mem. 10.7/139.8 GiB (7.7%) 20:35:52 [SUCCESS] Step 3 | 2.4s | Loss 0.9996 | Grad. Norm 7.4464 | LR 2.00e-05 | Throughput 6704 tokens/s | MFU 2.9% | Peak Mem. 10.7/139.8 GiB (7.7%) ``` Ten steps on a 0.6B model with a constant LR will not descend cleanly — the point is a visible, NaN-free curve. Occasionally the last metrics row lands during teardown; terminal status and the trainer's final log lines are the success signal. Teardown is automatic. The step-10 checkpoint and the `datasets/` folder stay on the volume. Wait for the run to end and the final volume sync to complete before downloading its checkpoint: ```bash theme={null} prime volumes get research runs//checkpoints/step_10/ ./step_10 ``` (Usage accounting shows `0 tokens / $0.00` for SFT — a reporting quirk, not free usage.) ## Online evals Add an `[eval]` block to score checkpoints against an environment while the run trains. The platform then runs an inference server alongside the trainer, which broadcasts its weights to it at each eval interval, plus one env server per eval source: ```toml theme={null} [eval] interval = 5 # evaluate every 5 trainer steps [[eval.source]] name = "reverse-text" env = { taskset = { id = "primeintellect/reverse-text" } } [inference] [inference.vllm] tensor_parallel_size = 1 ``` Sizing: inference GPUs come out of the same allocation as the trainer, so `num_train_gpus + num_infer_gpus` must fit `gpus_per_node` on a single node. On `multi_node`, set `num_infer_nodes` to at least 1 and add an `[inference.router]` block. ```toml theme={null} [deployment] type = "single_node" num_train_gpus = 4 num_infer_gpus = 4 # defaults to 1 gpus_per_node = 8 ``` Eval metrics land on the run's dashboard next to the loss curve, and each component gets its own log tab (`prime train logs --component inference`). The run stays `RUNNING` until the evals triggered by the final checkpoint have drained, so expect it to outlast the last trainer step. > [!NOTE] > > Online evals need a cluster on prime-rl-fft 0.24.0 or newer; dispatch is rejected with a clear error otherwise. Weight transport defaults to NCCL when `[inference]` is set, and to filesystem for LoRA runs. Traces are not yet wired for SFT eval rollouts. ## Troubleshooting > **Online evals rejected at dispatch** > > The cluster's chart predates online-eval support. Remove the `[eval]` block to run trainer-only SFT, or ask Prime support when the cluster will be upgraded. > > ```text theme={null} > Error: online evals ([eval]) are not supported by the prime-rl-fft chart this > deployment installs (needs 0.24.0 or newer). Remove the [eval] block to run > trainer-only SFT. > ``` > **\[inference] without \[eval]** > > The inference pool exists only to serve online-eval rollouts, so it needs an `[eval]` block to go with it. > > ```text theme={null} > Error: [inference] is only used for online evals — add an [eval] block or > remove it. > ``` > **Model not available on this cluster** > > Hosted SFT boots from the cluster model cache. Check `prime train models`, or ask Prime support to cache the model. If the weights are already on the volume, point `[model].name` at their `/volume/...` path instead — no cache entry is needed. > > ```text theme={null} > Error: HTTP 400: Model 'PrimeIntellect/Qwen3-0.6B' is not available on this > cluster. Reach out to Prime support to get it added. > ``` > **data.name requires --volume** > > Real data needs a volume. `data.type = "fake"` is the one no-volume path (synthetic data, connectivity smoke test). > > ```text theme={null} > Error: HTTP 400: data.name requires `--volume `. Create a volume with > `prime volumes create `, then rerun `prime train sft.toml --volume `. > ``` > **Dataset missing on the volume** > > The trainer reads `data.name` from the volume mount — a path that is empty or missing fails at startup. Re-run step 2 and check the name matches: the directory you create under `/volume/datasets/` in the SSH session is exactly the `datasets/` you point `data.name` at (relative paths resolve under `/volume`, so `datasets/` and `/volume/datasets/` are the same location). > **Volume ran out of space during download** > > Inside the shell, `hf download` fails when the session's disk is full. Exit, end the session, grow the volume, and re-run the download in a new session: > > ```bash theme={null} > prime volumes stop research > prime volumes expand research --size > prime volumes ssh research > ``` ## Limitations * **Requires CLI v0.9.0+** — older versions misroute SFT configs against the RL schema. * **SFT-shaped datasets** — `prompt`/`completion` or `messages` columns. * **Cached or volume-hosted models** — a cached HF id (`prime train models`; support can add others), or weights on the volume via a `/volume/...` `[model].name` path. * **Online evals need chart 0.24.0+** — on older clusters SFT is trainer-only and `[eval]` is rejected. No traces for eval rollouts yet. * **Checkpoints live on the volume** — use `prime train checkpoints ` to list saved checkpoints, then `prime volumes get research runs//checkpoints/` to download them after sync. ## Scaling up The same flow carries to multi-node — switch `[deployment]` to `multi_node` and add the parallelism knobs. The GLM-5.3 example below is a **4-step smoke config**: it checks that the multi-node path boots and the model fits in memory — it is not a throughput-tuned recipe. Save as `glm53-sft.toml`: ```toml theme={null} max_steps = 4 [env_vars] TRITON_CACHE_DIR = "/tmp/.cache/triton_cache" [model] name = "zai-org/GLM-5.3-BF16" impl = "custom" attn = "flash_attention_2" cp = 8 ep = 8 optim_cpu_offload = true [model.ac] freq = 1 [model.ac_offloading] max_inflight_activations = 5 [optim] type = "sign_sgd" lr = 5e-5 weight_decay = 0.01 [scheduler] type = "linear" warmup_steps = 50 decay_steps = 0 [renderer] name = "glm-5.3" [deployment] type = "multi_node" num_train_nodes = 8 gpus_per_node = 8 [model.debug] force_balanced_routing = true # debug only [ckpt] # save the final checkpoint interval = 4 keep_last = 1 [data] type = "sft" name = "datasets/intellect-3-sft-10k" # put it on the volume first (step 2); volume-relative splits = ["math"] batch_size = 8 micro_batch_size = 1 seq_len = 4096 ``` > [!NOTE] > > This config targets 8×8 H200 (64 GPUs). If you adapt it for real training: remove `force_balanced_routing` — it forces round-robin expert routing and is debug-only; with `warmup_steps = 50` a 4-step run never leaves warmup, so losses are warmup-dominated rather than convergence; the `[ckpt]` block saves one checkpoint at step 4, so size the volume for it (about 11–12 bytes per parameter, see [Choose a size](/hosted-training/volumes#choose-a-size)); with this linear schedule the LR ramps to its peak at step 50 — expect a transient loss spike near the peak before it settles (9.42 at step 55 in a 60-step run). See [Scaling](/prime-rl/scaling) and [Advanced](/prime-rl/advanced) for the knobs. > **[Hosted RL](/hosted-training/hosted-rl)** > > RL-style full-parameter training on a dedicated cluster — same CLI, same dashboard. > **[prime-rl Training](/prime-rl/training)** > > The underlying SFT trainer: config schema, dataset format, and local runs. # Models Source: https://docs.primeintellect.ai/hosted-training/models Which models and GPU types you can dispatch on Hosted Training The model list is cluster-dependent and changes as models are added, so the CLI is the source of truth: ```bash theme={null} prime train models ``` That gives you two things: * **Models** — the models available to dispatch * **On-Demand Capacity** — purchasable GPU types with their current `$/GPU-hour`, and whether a run **starts now** or **queues** Add `--output json` for machine-readable output. For pricing mechanics, see [Billing](/hosted-training/billing). ## The weight cache Runs boot from a cluster-side weight cache and never download weights at start-up. **A model that isn't cached can't be dispatched** — a config naming one fails at dispatch, before any GPU is reserved. To see only the cached models for a reserved cluster you hold: ```bash theme={null} prime train models ``` If the model you want is missing, [reach out](mailto:contact@primeintellect.ai) to get it added. ## GPU types To list every GPU type you can dispatch on: ```bash theme={null} prime train gpus ``` You can pin one explicitly at dispatch with `--gpu-type`, or let the platform pick. Placement favours clusters that already have your model cached, since a cache miss means a full weight download. ## Choosing a model **Validate first.** Start with the smallest model that exercises your environment end to end — a short run on a small model catches config and reward-function bugs for a fraction of the cost. Keep `max_steps` low and watch that the loss or reward moves at all. **Then scale.** Once the environment is proven, move to the model you actually care about and raise `batch_size` and `group_size` for a more stable reward signal. MoE models with small active parameter counts give strong performance per GPU-hour. Size the run with the `[deployment]` block — see [Hosted RL](/hosted-training/hosted-rl#config) for single-node and multi-node examples. ## Thinking mode Some model families support a thinking mode that produces extended chain-of-thought before the final answer. It tends to help on tasks that benefit from multi-step reasoning — math, code, logic — at the cost of longer completions, which means more tokens generated per rollout and more wall-clock per step. Thinking is controlled through the renderer and sampling config; see [prime-rl Configuration](/prime-rl/configuration). > **[Billing](/hosted-training/billing)** > > How GPU-hours are metered and where rates come from. > **[Hosted RL](/hosted-training/hosted-rl)** > > Config schema and dispatch reference. # Pause and resume runs Source: https://docs.primeintellect.ai/hosted-training/pause-resume Release a run's GPUs at a checkpoint and continue it later, on any cluster Pausing a run stops it at its next training step, saves a checkpoint of that step to the run's [volume](/hosted-training/volumes), and releases its GPUs. Resuming starts a new run from that checkpoint on whichever cluster has capacity, and training continues from the step after it. ## Quickstart Install and log in to [Prime CLI](/cli-reference/introduction) 0.16.0 or later. Pausing needs a run launched with `--volume`. **1. Launch a run on a volume** ```bash theme={null} prime train config.toml --volume research ``` **2. Pause it** ```bash theme={null} prime train pause ``` The run turns **PAUSING** while it finishes its current step, saves it, and syncs the volume. **3. Wait for PAUSED** ```bash theme={null} prime train get ``` Once the run is **PAUSED**, its GPUs are released and the meter stops. This usually takes a few minutes. **4. Resume it** ```bash theme={null} prime train resume ``` This starts a new run with its own ID, which reuses the paused run's config, volume, image, and GPU type and continues at the next step. ## Things to know * Only a RUNNING run can be paused, and only if it uses prime-rl image `v0.10.1.dev11` or later. The default image qualifies. A run without a volume can only be [stopped](/hosted-training/hosted-rl). * Pausing doesn't need a `[ckpt]` block. The pause saves its own checkpoint at `runs//checkpoints/step_/`. * A paused run can't be unpaused or restarted in place. Resume it as a new run instead. The new run writes its outputs to its own `runs//` directory on the same volume. * Secrets aren't kept after a run starts, so pass them again when you resume: `prime train resume -e WANDB_API_KEY`. * To resume on a different GPU type or image, pass `--gpu-type` or `--image-tag` to `prime train resume`. * To change the config, for example to raise `max_steps`, resume from your edited TOML with `prime train config.toml --resume-from `. Keep the same model, since the checkpoint is loaded as is. If you also pass `--volume`, it must name the paused run's volume. * The PAUSING phase is [billed](/hosted-training/billing) like a running run, because the run still holds its GPUs while it saves. A PAUSED run isn't billed. # Shared LoRA Training (Retired) Source: https://docs.primeintellect.ai/hosted-training/shared-lora-retired The shared multi-tenant LoRA training service has been retired and replaced by on-demand Hosted Training Shared Hosted Training for LoRA runs has been **retired**. The service trained a LoRA adapter on top of a shared, multi-tenant deployment and billed per token. It no longer accepts new runs, and its `.toml` config format is no longer dispatched. ## What to use instead [Hosted Training](/hosted-training/getting-started) now runs **on-demand**: your run gets its own GPUs, every parameter is trained, and you're [billed per GPU-hour](/hosted-training/billing). No reserved cluster required. Configs use the native [prime-rl](/prime-rl/configuration) schema rather than the old flat format — see [Hosted RL](/hosted-training/hosted-rl) for the config shape and [End-to-End Run](/hosted-training/end-to-end-run) for a walkthrough. ## Your existing adapters LoRA adapters from completed runs remain downloadable from the Prime platform. Adapter **deployment for inference has been removed**: adapters can no longer be served through the Prime Inference API, and the `prime deployments` CLI commands have been retired. To serve a fine-tuned model, self-host it with [prime-rl](/prime-rl/advanced#lora-training) on your own GPUs or on a [Model Factory Cluster](/model-factory/clusters). ## LoRA itself isn't going away Only the hosted shared service was retired. LoRA remains a supported, actively maintained feature of [prime-rl](/prime-rl/advanced#lora-training) for both RL and SFT, which you can run on your own GPUs or on a [Model Factory Cluster](/model-factory/clusters). > **[Getting Started](/hosted-training/getting-started)** > > Launch an on-demand Hosted Training run. > **[LoRA in prime-rl](/prime-rl/advanced#lora-training)** > > Train LoRA adapters on your own infrastructure. # Troubleshooting Source: https://docs.primeintellect.ai/hosted-training/troubleshooting Common issues and solutions for Hosted Training This page covers common issues you may encounter when using Hosted Training, along with their solutions. ## Environment Issues > **ModuleNotFoundError: No module named 'verifiers'** > > The verifiers library is not installed in your project. > > ```bash theme={null} > uv add verifiers > ``` > > Or if you're installing a specific environment: > > ```bash theme={null} > prime env install my-env > ``` > **load\_environment not found** > > This usually means a module name collision — your environment name conflicts with an existing Python package, or the environment wasn't installed correctly. > > **Solutions:** > > * Rename your environment to avoid conflicts > * Reinstall: `prime env install my-env` > * Check that your environment module exposes a `load_environment` function at the top level > **Environment not found on the Hub** > > The environment ID doesn't match any published environment. > > ```bash theme={null} > # Check the exact ID > prime env info owner/environment-name > > # Install a specific version > prime env install owner/environment-name@latest > ``` > > Make sure you're using the correct `owner/name` format and that you have access if it's a private environment. > **MissingKeyError when loading environment** > > The environment requires API keys that aren't set. The error message will list which keys are missing. > > **For local evaluation:** > > ```bash theme={null} > export OPENAI_API_KEY=sk-... > ``` > > **For Hosted Training**, the recommended approach is to link secrets to your environment via the [Environments Hub](/tutorials-environments/secrets). Alternatively, pass them at dispatch: > > ```bash theme={null} > prime train config.toml -e OPENAI_API_KEY # reads from your shell > prime train config.toml --env-file secrets.env # loads a whole file > ``` > > Or use the CLI to manage secrets: > > ```bash theme={null} > prime secret list # global secrets > prime env secret list my-env # per-environment secrets > ``` ## Training Issues > **Reward is always 0.0** > > The task is too hard for the model at its current capability level. The model can't solve any examples, so there's no reward signal to learn from. > > **Solutions:** > > * Try a larger or more capable model > * Use easier examples (filter your dataset or adjust environment args) > * Increase `[orchestrator.train.sampling].max_completion_tokens` — the model may need more space to reason > * Check your rubric implementation for bugs that might always return 0 > * Run a baseline evaluation first: `uv run vf-eval my-env -m -n 20 -r 1` > **Reward is always 1.0** > > The task is too easy — the model already solves everything. > > **Solutions:** > > * Use harder examples or a more challenging dataset split > * Add more demanding rubric criteria > * Use a smaller model that has more room to improve > **Reward is not changing / training seems stuck** > > There could be several causes: > > * **Low reward diversity:** If all rollouts for an example get the same reward, there's no contrast for the model to learn from. Increase `[orchestrator.train].group_size` (16–32) to get more variation. > * **Learning rate too low:** Try raising `[trainer.optim].lr` — Hosted Training runs typically sit in the `1e-6` to `1e-5` range, so move in small steps. > * **Batch size too small:** Larger batches provide more stable gradient estimates. Try `[orchestrator].batch_size = 512`. > * **Task mismatch:** The task may not be suitable for RL training. Ensure the reward function produces a meaningful gradient of scores, not just binary 0/1. > **Pydantic validation error in config** > > A field in your TOML config has the wrong type or an invalid value. > > **Common causes:** > > * String values not quoted: `name = Qwen/Qwen3-4B` → `name = "Qwen/Qwen3-4B"` > * Integer where float expected or vice versa > * Missing required sections like `[model]`, `[deployment]`, or `[[orchestrator.train.source]]` > > Double-check your config against the [prime-rl config reference](/prime-rl/configuration). > **Disk quota exceeded (Errno 122) on a volume** > > The run's [volume](/hosted-training/volumes) is full. Usually a checkpoint did not fit: a full fine-tuning checkpoint takes about 11–12 bytes per parameter, and the trainer writes a new one before it deletes the oldest. The trainer crashes and retries into the same limit until the run is marked failed. > > **Solutions:** > > * Set `keep_last = 1` or `2` in `[ckpt]`, or save less often with a larger `[ckpt].interval` > * Delete old runs' outputs: `prime volumes ssh --read-write`, then remove `/volume/runs/` > * Grow the volume or use a larger one > > See [Volume capacity](/hosted-training/volume-capacity) to size the volume. > **Model not available** > > The model you specified isn't currently supported for Hosted Training. > > ```bash theme={null} > # Check available models > prime train models > ``` > > Runs boot from a cluster-side weight cache, so a model that isn't cached can't be dispatched. See [Models](/hosted-training/models). ## CLI Issues > **prime: command not found** > > The CLI isn't installed or isn't on your PATH. > > ```bash theme={null} > # Install or reinstall > uv tool install prime > > # If already installed, upgrade > uv tool install -U prime > > # Verify > prime --version # Should be >= 0.5.15 > ``` > > If you installed with `uv tool install` and it's still not found, make sure `~/.local/bin` is in your PATH. > **Authentication failed / not logged in** > > Your CLI session may have expired. > > ```bash theme={null} > prime login > ``` > > This opens a browser window to re-authenticate. > **prime train fails immediately** > > Check the following: > > * Your config file is valid TOML (no syntax errors) > * The model is available: `prime train models` > * The environment ID is correct and accessible > * You're authenticated: `prime login` > * Your CLI is up to date: `uv tool install -U prime` ## Evaluation Issues > **Rate limit exceeded (429 errors)** > > Reduce concurrency when running evaluations: > > ```bash theme={null} > uv run vf-eval my-env -m openai/gpt-4.1-mini -n 100 -c 8 > ``` > > The `-c` flag controls maximum concurrent requests. Lower it if you're hitting rate limits. > **ThinkParser failures with Qwen3 models** > > Qwen3 and DeepSeek-R1 models have chat templates that automatically remove `` tags from message history. This conflicts with `ThinkParser`. > > **Solution:** Use `MaybeThinkParser` or `Parser` instead of `ThinkParser` in your environment: > > ```python theme={null} > # Instead of: > parser = vf.ThinkParser(extract_fn=my_fn) > > # Use: > parser = vf.MaybeThinkParser(extract_fn=my_fn) > ``` > **Evaluation results look wrong or inconsistent** > > * **Check `rollouts_per_example`:** Low values (1–2) produce noisy results. Use at least 3–5 for reliable metrics. > * **Check `num_examples`:** Very small sample sizes can be misleading. > * **Check sampling temperature:** High temperatures produce more variation between runs. > * **Check your rubric:** Make sure reward functions handle edge cases (empty responses, malformed outputs, etc.). ## Environment Development Issues > **Environment works locally but fails in Hosted Training** > > Common causes: > > * **Missing dependencies:** Make sure all required packages are listed in your environment's `pyproject.toml` > * **Missing secrets:** API keys available locally may not be set for hosted runs. Link them to your environment via the [Environments Hub](/tutorials-environments/secrets), or pass them at dispatch with `-e` / `--env-file`. > * **Hardcoded paths:** Avoid absolute file paths in your environment code > * **Network access:** Some external APIs may not be reachable from the hosted environment > **How do I debug my environment?** > > Start with a local evaluation using verbose output: > > ```bash theme={null} > uv run vf-eval my-env -m openai/gpt-4.1-mini -n 5 -r 1 -v > ``` > > The `-v` flag enables verbose logging. You can also test your environment directly in Python: > > ```python theme={null} > from verifiers import load_environment > > env = load_environment("my-env") > # Inspect the dataset, rubric, etc. > ``` ## Getting Help If your issue isn't covered here: * **Discord:** Join the [Prime Intellect Discord](https://discord.gg/ZTFydGWPKj) for community support and Q\&A * **Research Support:** Fill out the [research support form](https://form.typeform.com/to/iYn9UliG) for hands-on assistance * **Feedback:** Use the thumbs-up/down on any docs page to let us know what's helpful or missing ## Missing traces or upload failures Check the workspace, upload setting, API permissions, and storage limit. Training completion does not by itself confirm that every trace uploaded. See [Traces troubleshooting](/traces/managing-traces#troubleshooting). For older samples, see [legacy storage retirement and exports](/traces/legacy-storage). # Volume capacity and checkpoint sizing Source: https://docs.primeintellect.ai/hosted-training/volume-capacity Size a volume for your checkpoints, and fix runs that fail with Disk quota exceeded A [volume](/hosted-training/volumes) has a fixed size. Runs that write past it fail. Full fine-tuning checkpoints are large, so size the volume before you launch a run. ## What fills a volume | Content | Where | When it is removed | | - | - | - | | Trainer checkpoints | `runs//checkpoints/step_N/` | When the run's retention settings delete them (see below) | | Data you uploaded | for example `datasets/`, `models/` | Only when you delete it | | Outputs of earlier runs | `runs//` | Only when you delete them; they stay after a run ends | | RL rollout traces | under the run's directory | Never during the run; they grow until the run ends | Every run that uses the volume shares the same space. ## How many checkpoints are on disk These `[ckpt]` settings control how many checkpoints a run keeps. RL configs use the same fields in the shared `[ckpt]` block. | Setting | Effect | | - | - | | `interval` | Save a checkpoint every N steps. When unset, the run saves one checkpoint at the end of training. | | `keep_last` | Keep only the N most recent checkpoints. When unset, **every checkpoint is kept**. | | `keep_interval` | Also keep every checkpoint whose step is a multiple of N, permanently. | The trainer writes a new checkpoint **before** it deletes the oldest one. So at peak the run holds `keep_last + 1` checkpoints, plus any pinned by `keep_interval`. ## How big one checkpoint is A trainer checkpoint holds the full training state: model weights in fp32 and the optimizer state. Plan for about **11–12 bytes per parameter**. Measured saves: \~341 GB for Qwen3-30B-A3B (30.5B parameters) and \~7.2 GB for a 0.6B model. | Model size | One checkpoint (about 11–12 bytes/param) | | - | - | | 0.6B (measured) | \~7.2 GB | | 8B | \~90–100 GB | | 30B (measured: Qwen3-30B-A3B, 30.5B params) | \~341 GB | | 100B | \~1.1–1.2 TB | The bf16 weights alone are 2 bytes per parameter (\~57 GiB for Qwen3-30B-A3B). That is a lower bound and the size of exported weights. It is not the size of a trainer checkpoint. > [!NOTE] > > `ckpt.skip_optimizer = true` does not make checkpoints smaller. It only skips **loading** the optimizer state when a run resumes. The checkpoint is still saved with the full state. ## Size the volume ```text theme={null} required ≈ (keep_last + 1) × 11–12 bytes × parameters + uploaded datasets and models + outputs of other runs on the volume + about 20% headroom ``` If `keep_last` is unset, multiply by the number of checkpoints the run will save instead. **Example.** Qwen3-30B-A3B (30.5B parameters) with `keep_last = 2`: * One checkpoint is about 341 GB. Peak is 3 checkpoints: about 1 TB. * With the formula (11–12 bytes per parameter, 20% headroom): about 1.2 TB, plus your datasets. Use a `1536Gi` (1.5 TiB) volume. Sizes are whole numbers of `Gi` or `Ti`, so write 1.5 TiB as `1536Gi`. * On a `200Gi` volume (about 215 GB), even the **first** checkpoint does not fit. The run fails at its first save. For RL runs, use the same checkpoint math, then add room for rollout traces. Traces are written in chunks of about 5 GiB. That is the file size per chunk, not a limit: traces keep growing for as long as the run trains. Long RL runs need extra headroom, and old runs' traces should be cleaned up. ## The size is a hard quota The size you set with `--size` or `--volume-size` is a hard limit. A write past it fails with one of these errors: ```text theme={null} OSError: [Errno 122] Disk quota exceeded OSError: [Errno 28] No space left on device ``` Check what uses the space (Prime CLI 0.13.0 or later): ```bash theme={null} prime volumes du training-outputs -h # whole volume, and usage against its size prime volumes du training-outputs /runs -h # one directory ``` The volume's stored contents update when a run or read-write session ends, so `du` does not show what a run that is still going has written. A volume that already holds more than its size cannot start a run: the run fails before training with `volume contents (...) exceed the volume size (...); run prime volumes expand`. Expand the volume or delete data, then launch again. ## What happens when a volume fills 1. The checkpoint save fails and the trainer crashes. 2. The trainer restarts from the last good checkpoint (or from the start, if there is none), trains to the next save, and hits the same limit again. Each retry spends GPU time. 3. After repeated crashes, the run is marked failed with a disk-quota error. The run does not recover on its own. Free space or use a larger volume, then launch again. ## Fix or prevent a full volume * **Keep fewer checkpoints.** Set `keep_last = 1` or `2` in `[ckpt]`. * **Save less often.** Raise `[ckpt].interval`, or leave it unset to save only at the end. * **Delete old outputs.** Open a read-write session and remove runs you no longer need: ```bash theme={null} prime volumes ssh training-outputs --read-write rm -rf /volume/runs/ ``` Do not delete a checkpoint that another run resumes from with `[resume].dir`. * **Grow the volume.** Expand it with `prime volumes expand --size ` (see [Make a volume bigger](/hosted-training/volumes#make-a-volume-bigger)). # Volume-hosted models Source: https://docs.primeintellect.ai/hosted-training/volume-models Train from model weights stored on your volume instead of the model cache > [!NOTE] > > Volume-hosted models are part of the [Volumes](/hosted-training/volumes) beta. Hosted SFT and RL runs can start from Hugging Face-format weights on a [volume](/hosted-training/volumes), including private models and weights exported from your own fine-tunes. Point `[model].name` at their absolute `/volume/...` path and attach the volume. No model-cache entry is needed. Use this for: * **Weights that aren't on Hugging Face**, such as private or proprietary checkpoints. * **Continuing your own fine-tunes** from weights you exported to Hugging Face format. ## Quick start Create a volume and upload a complete Hugging Face model directory, including `config.json`, safetensors weights, and tokenizer assets: ```bash theme={null} prime volumes create research prime volumes put research ./qwen3-hf models/ ``` Point the config at them: ```toml theme={null} max_steps = 100 [ckpt] interval = 50 keep_last = 2 [model] name = "/volume/models/qwen3-hf" # Qwen3 weights at this path [data] name = "datasets/my-dataset" # the dataset can live on the same volume type = "sft" seq_len = 4096 [deployment] num_train_gpus = 1 [renderer] name = "prime-qwen3" # match the staged model's family ``` Launch with the volume: ```bash theme={null} prime train sft.toml --volume research ``` Size `[deployment]` for your model as described in [Hosted SFT](/hosted-training/hosted-sft#3-write-the-config). This example uses Qwen3 weights; choose the renderer for your model family. ## Use the model in an RL run Start with a complete [RL training config](/hosted-training/end-to-end-run#step-4-create-a-training-config) and replace its shared model name: ```toml theme={null} [model] name = "/volume/models/qwen3-hf" [orchestrator.renderer] name = "prime-qwen3" ``` The shared name supplies the trainer, orchestrator, and inference model. If your config overrides model names for individual roles, change those names to the same directory too. Then launch with `prime train config.toml --volume research`. Set the renderer explicitly for the model family. A filesystem path cannot identify the family from a Hugging Face model ID; the Qwen3 example above uses `prime-qwen3`. RL configs with `[trainer]` and `[orchestrator]` blocks support volume model paths; a cached Hugging Face ID is not required for these paths. ## Rules | Requirement | Rule | | - | - | | Volume required | The run must use the volume: `--volume `, or `volume = ""` in the config. | | Path | Starts with `/volume/` and has at least one folder after it. Relative paths don't work here, unlike `data.name`. | | Path characters | Each part starts with a letter or digit, followed by letters, digits, `.`, `-`, or `_`. `.` and `..` aren't allowed. | | Weight format | A complete Hugging Face model directory, including config, weights, and tokenizer assets. A sharded training checkpoint is not a model directory: see [below](#continue-from-a-previous-runs-checkpoint). | | Model cache | Not used, and not checked. | The whole volume is mounted read-write, including earlier `runs/` directories. The trainer writes weight conversions to scratch space rather than overwriting the source model. ## Continue from a previous run's checkpoint Run checkpoints are in prime-rl's sharded format, so they can't be used directly as `[model].name`. [Export a checkpoint to Hugging Face format](/hosted-training/end-to-end-run#export-to-hf-format), upload the exported directory under `models/`, and use its `/volume/models/...` path for a new SFT or RL run. Starting from exported weights begins a new run; it does not restore the previous optimizer or training step. To resume the full training state, set `[resume].dir` to the checkpoint's step directory in the run TOML: ```toml theme={null} [resume] dir = "/volume/runs//checkpoints/step_" ``` ## See also * [Volumes](/hosted-training/volumes): create, upload, and download. * [Hosted SFT](/hosted-training/hosted-sft): the walkthrough this builds on. * [Hosted RL](/hosted-training/hosted-rl): RL configs and dispatch options. # Volumes (Beta) Source: https://docs.primeintellect.ai/hosted-training/volumes Keep datasets, models, and checkpoints across Hosted Training runs > [!NOTE] > > Volumes are in **beta** for Hosted RL and Hosted SFT. A volume is named cloud storage for your training data and results. Upload datasets and models to it, use it in training runs, and download the checkpoints each run saves there. A volume belongs to your team (or to you, with no team selected) and isn't tied to any cluster. > [!NOTE] > > Legacy staging is being retired; volumes work the same on every cluster. ## What you can do with and without a cluster allocation | Task | Without a cluster allocation or team-owned cluster | With one | | - | - | - | | Create, list, grow, and delete volumes | Yes | Yes | | Upload and download files (`put` / `get`) | Yes | Yes | | Open a shell on a volume (`ssh`) | No | Yes | | Use a volume in a training run (`--volume`) | Yes, on [on-demand](/hosted-training/billing) capacity (beta) for GPU types that support it | Yes | On-demand training is in beta and needs beta access for your account or team. On demand, a volume works only on GPU types whose clusters support shared storage. On any other GPU type, the run is refused with a message that names the type. Pick another GPU type with `--gpu-type`, or run without `--volume`. Shells need a cluster allocation for your account or team, or a cluster your team owns. On-demand capacity alone doesn't include SSH. Personal accounts can't own clusters, so they need an allocation. Ask Prime support if you need one. ## Quickstart Install and log in to [Prime CLI](/cli-reference/introduction) 0.11.0 or later. Volumes use the team set in your CLI (`prime config set-team-id `). With no team set, they belong to your personal account. **1. Create a volume** ```bash theme={null} prime volumes create research --size 500Gi ``` **2. Upload a dataset** ```bash theme={null} prime volumes put research ./my-data datasets/ ``` This creates `datasets/my-data` on the volume. Training runs see it at `/volume/datasets/my-data`. **3. Optional: open a shell on the volume** Needs [a cluster allocation or a cluster your team owns](#what-you-can-do-with-and-without-a-cluster-allocation) and an [SSH key](#set-up-your-ssh-key). ```bash theme={null} prime volumes ssh research ``` After editing, exit the shell, run `prime volumes stop research `, and wait for the final sync. Exiting alone leaves the session open and blocks training on the volume. **4. Train with the volume** Add a [checkpoint configuration](#checkpoint-settings) to save weights, then: ```bash theme={null} prime train config.toml --volume research ``` **5. Download your checkpoints** Wait for the run to end and its volume sync to finish, then: ```bash theme={null} prime volumes get research runs//checkpoints/ ./checkpoints ``` **6. Delete the volume when you're done** ```bash theme={null} prime volumes delete research ``` ## Things to know * Each run writes checkpoints under `runs//checkpoints/`. Previous runs are visible in the next run or shell at `/volume/runs/`. Direct `put` uploads to `runs/` are refused. * A volume has one writer at a time: a training run, a read-write shell, or an upload. Wait for that writer to finish syncing before starting another. Read-only shells can coexist with a writer, but see the last saved copy. * Changes made by a run or shell reach cloud storage when it ends. There is no periodic sync. Downloads during a run can return the previous saved contents. * Each run gets a disk the size of your volume. It must fit the whole volume, including earlier runs, plus new checkpoints. See [Choose a size](#choose-a-size). * Volumes are billed for their full size, whether or not anything uses them. See [Pricing](#pricing). ## Create a volume Names use 1 to 40 lowercase letters, digits, and hyphens, and start and end with a letter or digit. The default size is `5Ti`, and the maximum is `20Ti`. > [!WARNING] > > You pay for the size you set, not the data you store. The default `5Ti` costs about \$460.80 per month even when empty. Pass `--size` to pick a smaller volume; you can [grow it later](#make-a-volume-bigger). See [Pricing](#pricing). ```bash theme={null} prime volumes create research prime volumes create experiments --size 500Gi ``` The volume is ready as soon as the command returns. If you have a cluster allocation (or your team owns a cluster) and an SSH key, `create` also starts a read-only shell session in the background. That session stops after 30 minutes of no use. Pass `--no-warm` to skip it. List your volumes: ```bash theme={null} prime volumes list ``` ```text theme={null} ┏━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓ ┃ Name ┃ Size ┃ Status ┃ Created ┃ Created by ┃ ┡━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩ │ research │ 500Gi │ CREATED │ 2026-10-12T16:05:11.482000+00:00 │ Jane Doe │ └──────────┴───────┴─────────┴──────────────────────────────────┴────────────┘ ``` Add `-o json` for JSON output. ## Upload data `put` uploads a file or folder. Paths on the volume start at its root: ```bash theme={null} prime volumes put research ./my-data datasets/ # creates datasets/my-data prime volumes put research ./my-data/ datasets/my-data # same, copying the folder's contents prime volumes put research ./model.safetensors models/my-model/ ``` * A trailing `/` on the volume path means "into this folder". * A trailing `/` on a local folder copies what's inside it, not the folder itself. * Volume paths can use letters, digits, and `._-@%+=,:`. For other names, use a [shell](#open-a-shell-on-your-volume). * Symlinks inside a folder are skipped. * You can't upload to `runs/`. Uploads go straight to cloud storage, so they don't need a cluster allocation or an SSH key. While an upload runs, nobody can start another writer or delete the volume. A training run or someone else's read-write shell blocks uploads until it finishes syncing. If you have your own read-write shell open, `put` can upload through it instead; those changes reach cloud storage when you end the session. ## Download data ```bash theme={null} prime volumes get research datasets/my-data/ ./my-data prime volumes get research / ./everything ``` The local destination defaults to the current folder. Direct downloads read the saved copy in cloud storage. Wait for a run or read-write shell to finish syncing to retrieve its latest changes. `get` can read ordinary files through your own read-write shell, but downloads under `runs/` always use cloud storage. ## Open a shell on your volume A shell is handy for downloading data on the cluster's fast network (for example, with `hf download`), or for looking around and editing files. It needs [a cluster allocation or a cluster your team owns](#what-you-can-do-with-and-without-a-cluster-allocation) and an [SSH key](#set-up-your-ssh-key). ```bash theme={null} prime volumes ssh research # read-write (the default) prime volumes ssh research --read-only ``` When the session starts, the whole volume is copied to the machine, including `runs/`, and you find it at `/volume`. A large volume takes a while to copy. The session becomes ready after the download completes. * **Read-write:** changes are saved back when the session ends. Files you delete in the shell are deleted from cloud storage too. A training run or another read-write shell holds the writer lock until its final sync completes. * **Read-only:** nothing is saved back. These sessions can coexist with a writer, subject to the session limit. They read the saved contents from when the session started. Exiting the shell doesn't end the session. It ends after 30 minutes with no connection, or after 24 hours. To end it now: ```bash theme={null} prime volumes sessions research prime volumes stop research ``` Only you can connect to or stop your sessions, even on a team volume. Wait for the final sync after stopping a read-write session before downloading its changes or launching a training run on the same volume. ### Set up your SSH key Shells accept one key: your account's **primary** SSH key. The CLI must point at the matching private key. **1. Get a key pair** Use an existing pair, or create one: ```bash theme={null} ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 ``` **2. Add the public key to your account** Open [Dashboard → Tokens → SSH Keys](https://app.primeintellect.ai/dashboard/tokens?tab=ssh-keys) and paste the contents of `~/.ssh/id_ed25519.pub`. A new key becomes your primary key. To switch later, click **Set Primary** on another key. **3. Point the CLI at the private key** ```bash theme={null} prime config set-ssh-key-path ~/.ssh/id_ed25519 ``` The default is `~/.ssh/id_rsa`. `prime volumes ssh` prints `Using SSH key: ` before it connects. If you never added a key, you can use the key pair under **System generated keys** on the same page: download the private key, run `chmod 600` on it, and point `prime config set-ssh-key-path` at it. Once you add your own key, the system-generated key stops working for shells. ## Use a volume in a training run Pass `--volume` to a [Hosted RL](/hosted-training/hosted-rl) or [Hosted SFT](/hosted-training/hosted-sft) run: ```bash theme={null} prime train config.toml --volume research ``` Or set `volume = "research"` at the top of the TOML, before any tables. If the volume doesn't exist, the CLI creates it. Use `--volume-size` to choose its size; without it, the new volume gets the default `5Ti` size and is [billed](#pricing) for it. When the run starts, the whole volume is downloaded and mounted read-write at `/volume`. For example, a Hosted SFT `data.name = "datasets/my-data"` reads `/volume/datasets/my-data`. Earlier outputs are available under `/volume/runs/`. The run's changes are saved to cloud storage when it ends, after the training pods stop. The final sync runs when the run completes, stops, fails, or [pauses](/hosted-training/pause-resume), so checkpoints survive `prime train stop` and crashes. End any read-write shell and wait for its final sync before launching the run. A training run holds the volume's writer lock until its own sync completes. ### Train from weights already on the volume Both Hosted SFT and RL runs can load Hugging Face-format model weights from an absolute `/volume/...` path. Set `[model].name` in a Hosted SFT config, or set the shared `[model].name` in an RL config with `[trainer]` and `[orchestrator]` blocks: ```toml theme={null} [model] name = "/volume/models/my-model" ``` Attach the volume when launching: ```bash theme={null} prime train config.toml --volume research ``` The directory must contain the model configuration, weights, and tokenizer assets. It does not need an entry in the cluster model cache. A sharded training checkpoint is a different format; [export it to Hugging Face format](/hosted-training/end-to-end-run#export-to-hf-format) before using it as a model directory. See [Volume-hosted models](/hosted-training/volume-models) for config examples and path rules. ### Checkpoint settings Checkpointing is off unless you add `[ckpt]`. An empty `[ckpt]` block saves the final checkpoint; set `interval` to save during training too: ```toml theme={null} max_steps = 100 [ckpt] interval = 50 # save a checkpoint every 50 steps keep_last = 2 # keep the 2 newest checkpoints ``` | Setting | What it does | | - | - | | `interval` | How often the run saves a checkpoint. The final checkpoint is also saved. | | `keep_last` | How many recent checkpoints to keep. Pruned checkpoints are not recovered by the final volume sync. | | `keep_interval` | Preserve checkpoints at multiples of this step in addition to the recent ones. It does not control upload timing. | If you set only `keep_interval`, the final checkpoint can be pruned when its step is not a multiple of that interval. Set `keep_last` to at least `1` to keep the final checkpoint too. > [!NOTE] > > Neither `keep_interval` nor `keep_last` is required. All files on `/volume` are synced when the run ends. RL runs write checkpoints, logs, and other run outputs there. Hosted runs currently do not save the resolved config needed for [checkpoint conversion](/hosted-training/end-to-end-run#export-to-hf-format). Hosted SFT writes checkpoints there; its other outputs remain on the run's scratch disk. ## Get your checkpoints back Each run saves its checkpoints to `runs//checkpoints/step_/`: ```bash theme={null} prime volumes get research runs//checkpoints/ ./checkpoints prime volumes get research runs//checkpoints/step_100/ ./step_100 ``` Wait for the final volume sync before downloading a new run's outputs. Complete trainer checkpoints contain `trainer/.metadata`. To load checkpoints outside Prime, convert them first: see [Export to HF format](/hosted-training/end-to-end-run#export-to-hf-format). ## Choose a size Each run gets a disk the size of your volume. It must fit: * all existing files, including datasets, models, and earlier `runs/` outputs, plus * `keep_last + 1` checkpoints (the run writes a new one before it deletes the oldest), plus * about 20% extra. The `keep_last + 1` estimate assumes `keep_last` is set and `keep_interval` is unset. If you also preserve checkpoints with `keep_interval`, include those extra checkpoints. Without retention limits, size for every checkpoint the run saves. A full fine-tuning checkpoint is about **11–12 bytes per parameter** (measured: \~341 GB for a 30B save, \~7.2 GB for a 0.6B save): | Model size | One checkpoint | | - | - | | 0.6B | \~7.2 GB | | 8B | \~100 GB | | 30B (measured: Qwen3-30B-A3B) | \~341 GB | | 100B | \~1.2 TB | For example, Qwen3-30B-A3B with `keep_last = 2` holds up to 3 checkpoints, about 1 TB. With 20% extra and your datasets, use `1536Gi` (1.5 TiB). On `200Gi`, even the first checkpoint doesn't fit. `ckpt.skip_optimizer = true` doesn't make checkpoints smaller. For the full sizing formula, the peak checkpoint count, and how to fix a run that fails with `Disk quota exceeded`, see [Volume capacity](/hosted-training/volume-capacity). > [!WARNING] > > Deleting saved files or checkpoints inside a read-write shell removes them from cloud storage when the session ends. Check what you need to keep before removing earlier runs to free space. ### Check disk usage ```bash theme={null} prime volumes du research ``` `prime volumes du` (CLI 0.13.0 or later) shows how much of the volume's saved contents is in use. It reads cloud storage, so a live run's or session's changes appear only after it ends and syncs. ## Make a volume bigger ```bash theme={null} prime volumes expand research --size 2Ti ``` Volumes can grow up to `20Ti` but can't shrink. Runs and shells already going keep their old size. New ones get the new size. Billing switches to the new size from the moment the expand completes. Expanding runs the same [wallet checks](#pricing) as creating a volume. ## Pricing Volume storage costs **\$0.09 per GiB per month** (\$0.000125 per GiB per hour, with a month counted as 30 days). * **You pay for the volume's size, not its contents.** A `500Gi` volume costs the same whether it holds 1 GiB or 500 GiB. `prime volumes du` shows how much you use. * **Billing runs from creation to deletion**, whether or not a run or shell is using the volume. It is prorated, with no minimum. Deleting the volume stops the charge. * **Team volumes bill to the team's wallet**; personal volumes bill to yours. * Volume storage is separate from the [GPU-hours](/hosted-training/billing) a training run uses. | Size | Per month | | - | - | | `100Gi` | \$9.00 | | `500Gi` | \$45.00 | | `1Ti` | \$92.16 | | `1536Gi` (1.5 TiB) | \$138.24 | | `5Ti` (default) | \$460.80 | | `20Ti` (maximum) | \$1,843.20 | Creating or expanding a volume checks your wallet the same way a run does: you need a spendable balance, and the new hourly cost must fit within your spend limit and any team-member spending limits. If it doesn't, the command fails with `You've reached your Spend Limit` or `Insufficient funds in the wallet`. Add funds or raise the limit in the [Billing dashboard](https://app.primeintellect.ai/dashboard/billing), or choose a smaller size. Track volume charges in the [Billing dashboard](https://app.primeintellect.ai/dashboard/billing). ## Delete a volume > [!WARNING] > > Deleting a volume deletes everything on it, including every run's checkpoints. ```bash theme={null} prime volumes delete research ``` Add `--yes` to skip the confirmation. Only team admins can delete team volumes. You can't delete a volume while a run uses it, a shell is open on it, or an upload is running. Stop the run (`prime train stop `) or the shell (`prime volumes stop`), then try again. If the status shows `DELETE FAILED`, run `prime volumes delete` again. ## Troubleshooting | Error | What it means | What to do | | - | - | - | | `SSH sessions need a cluster allocation or a team-owned cluster with shared storage, and this team has neither (on-demand capacity doesn't include SSH).` (for a personal account: `...a cluster allocation with shared storage, and this account has none...`) | Shells need a cluster allocation or a cluster your team owns. On-demand capacity alone doesn't include SSH. | Use `put` and `get` instead, or ask Prime support for a cluster allocation. Until the newer wording ships, you may see the older message `no cluster assigned to you has ReadWriteMany storage, which an SSH session needs to stage the volume; ask Prime support for one`, which means the same thing. | | `On-demand training is in beta. Contact Prime support to request beta access for this account.` | Your account or team has no cluster allocation, so the run would use on-demand capacity, which is in beta. | Ask Prime support for beta access, or for a cluster allocation. | | `No on-demand gpu_type= cluster has ReadWriteMany storage, which a --volume run needs. Try another GPU type, or run without --volume.` | On demand, that GPU type doesn't support volumes. | Choose another GPU type with `--gpu-type`, or run without `--volume`. | | `volume '' has an active read-write session (...); use --read-only, or end that session first` | Someone else has a read-write shell open. | Use `--read-only`, or ask them to end their session. | | `volume '' has a live read-write SSH session (...); put through that session or end it first` | You tried to upload while someone else has a read-write shell open. | Upload from inside that shell, or wait for it to end. | | `an upload to volume '' is in progress until