Skip to main content

Merge & Retrain API

Merge and retrain-on-top are first-class platform operations: any account can (a) merge one or more trained models/adapters into a base, and (b) retrain on top of any trained or merged model — through the normal product APIs, with nothing hardcoded.

Both operations resolve their inputs generically, by catalog reference (an ArtifactRef), never by hand-written storage paths. The result of either operation is an ordinary model_version: servable through the normal inference path and reusable as a base — with full lineage recorded.

Base URL: https://api.colabhive.com/api/builder/v1

All endpoints require an API key; the account is derived from that key. X-Account-ID is optional and, if supplied, must match the key's account. Merge and retrain runs count against your account's training quota (a merge is a job, like any other).


ArtifactRef

A base, adapter, or source is always referenced as an ArtifactRef — a discriminated union on type. The gateway resolves the reference to a storage location and metadata (framework, precision, detected base) from the catalog, validates ownership, and builds the internal staging. You never manipulate object-store paths directly.

typeShapeMeaning
hf{ "type": "hf", "repo_id": "Qwen/Qwen2.5-7B-Instruct", "revision": "main" }A HuggingFace repo. revision defaults to main. Travels as the base repo; not staged.
model_version{ "type": "model_version", "version_id": "<uuid>" }Any trained or merged model_version you own or that is public.
job{ "type": "job", "job_id": "<uuid>" }Shortcut: uses the output version of a training job.
storage{ "type": "storage", "storage_url": "s3://..." }Advanced escape-hatch: a direct s3:// URL. Not the normal path.

Ownership & visibility

Resolution reuses ColabHive's existing visibility mechanism — no new permission model is invented:

  • Public model version → any account may use it as a base/adapter/source.
  • Private → only the owner. To let others build on your model, publish it (make it public).
  • The lineage always records the origin, so provenance survives even across accounts.

A storage-type reference has no catalog entry to check visibility against — it is trusted to the authenticated caller and intended for advanced use only.


Merge models

POST /training/merges

Merges N trained models/adapters into a base and produces a new full model_version. Merging is weight arithmetic and runs on CPU — it does not consume GPU. The output is framework=transformers, precision=bf16 (chains are never re-quantized), servable and reusable as a base.

For retry-safe creation, send Idempotency-Key with a UUID value. The key is scoped to the account and operation for 24 hours. Repeating the same request returns the original response with Idempotency-Replayed: true; reusing the key with a different body returns HTTP 409.

Request:

{
"name": "qwen-domain-base",
"base": { "type": "hf", "repo_id": "Qwen/Qwen2.5-7B-Instruct" },
"method": "adapter_merge",
"adapters": [ { "type": "model_version", "version_id": "ADAPTER_VERSION_UUID" } ],
"sources": [],
"weights": null,
"precision": "bf16",
"hardware_preference": "cpu_only"
}
FieldTypeDefaultNotes
namestring— (required)Name of the resulting model_version.
baseArtifactRefauto-detectedOptional. If omitted, the base is auto-detected from the first adapter/source.
methodstringadapter_mergeadapter_merge | slerp | ties | dare. Extensible.
adaptersArtifactRef[][]1..N adapters. Required for adapter_merge.
sourcesArtifactRef[][]1..N full models. Required for slerp / ties / dare.
weightsarraynullOptional per-method weights/parameters (e.g. TIES/DARE ratios).
precisionstringbf16Default bf16; never re-quantize in a chain.
hardware_preferencestringcpu_onlyMerge is weight arithmetic → CPU.

Method rules (validated):

  • method must be one of the allowed values, or 400.
  • At least one of adapters or sources is required, or 400.
  • adapter_merge requires ≥ 1 adapter; slerp/ties/dare require ≥ 1 source.

Response 201:

{
"job_id": "MERGE_JOB_UUID",
"produces_version_id": null,
"status": "queued",
"lineage": { "base_model_version_id": "PRIMARY_INPUT_VERSION_UUID" }
}

produces_version_id is null at creation and is populated once the job completes and the registrar creates the model_version. Poll the job with GET /training/runs/{job_id}.

cURL:

curl -X POST "https://api.colabhive.com/api/builder/v1/training/merges" \
-H "Content-Type: application/json" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"name": "qwen-domain-base",
"adapters": [{"type": "model_version", "version_id": "ADAPTER_VERSION_UUID"}]
}'

Multi-source example (TIES):

{
"name": "ensemble-base",
"method": "ties",
"sources": [
{ "type": "model_version", "version_id": "MODEL_A_VERSION_UUID" },
{ "type": "model_version", "version_id": "MODEL_B_VERSION_UUID" }
],
"weights": [0.5, 0.5]
}

Retrain on top

Retrain-on-top is an extension of POST /training/runs: the base of the run can be an existing trained or merged model_version (or a job/storage/HF repo) instead of the base defined by the model_config.

Two first-class optional fields are added to the training-run create request:

FieldTypeNotes
baseArtifactRefOptional. model_version / job / storage → retrain-on-top (staged as the base). hf or omitted → current behavior (base from the model_config).
parent_job_idstring | nullOptional lineage parent. Derived automatically from base if not provided.

Request:

{
"job_name": "domain-expert-v2",
"model_config_id": "MODEL_CONFIG_UUID",
"dataset_id": "DATASET_UUID",
"base": { "type": "model_version", "version_id": "MERGED_OR_TRAINED_VERSION_UUID" },
"parent_job_id": null,
"hyperparameters": { "learning_rate": 0.0002, "num_epochs": 3, "adapter": "qlora" }
}
  • hyperparameters now carries only hyperparameters (learning rate, epochs, adapter, sequence length, …). The base and staging are derived from the base field — you no longer smuggle them through hyperparameters.
  • Back-compat: if stage_artifacts / base_model_version_id / base_model still arrive inside hyperparameters while base is also set, the first-class base takes precedence and a deprecation warning is logged.

The response is the created run (see Create Training Run). The resulting model_version records base_model_version_id and parent_job_id for lineage.


Model lineage

GET /models/{model_id}/lineage

Returns the base→derivatives graph for a model, walking base_model_version_id up (ancestors/bases) and down (derivatives). This powers the "use as base" UI and full history traceability — provenance holds even after models are renamed, because identity is the version_id, not the name.

Response:

{
"model_id": "MODEL_UUID",
"model_name": "qwen-domain-base",
"root_version_ids": ["VERSION_UUID_1"],
"nodes": [
{
"version_id": "VERSION_UUID_1",
"base_model_version_id": "PARENT_VERSION_UUID",
"restricted": false,
"model_id": "MODEL_UUID",
"model_name": "qwen-domain-base",
"version_name": "v1",
"framework": "transformers",
"precision": "bf16",
"training_job_id": "JOB_UUID",
"created_at": "2026-07-23T00:00:00Z"
},
{
"version_id": "PARENT_VERSION_UUID",
"base_model_version_id": null,
"restricted": true
}
],
"edges": [
{ "from": "PARENT_VERSION_UUID", "to": "VERSION_UUID_1" }
]
}
  • Each node always exposes its version_id and edges (complete traceability). Rich metadata appears only for nodes the caller may see; nodes you cannot see are returned with "restricted": true and no metadata.
  • 403 if the target model is private and not owned by the caller; 404 if not found.

Rename a model version

PATCH /models/{model_id}/versions/{version_id}

A model version's name is a mutable label. Renaming does not change its identity (version_id) or break lineage. Only the owner can rename.

Request:

{ "name": "qwen-domain-base-approved" }

Response:

{
"version_id": "VERSION_UUID",
"model_id": "MODEL_UUID",
"version_name": "qwen-domain-base-approved",
"framework": "transformers",
"precision": "bf16",
"base_model_version_id": "PARENT_VERSION_UUID",
"training_job_id": "JOB_UUID"
}

403 if the caller is not the owner; 404 if the version does not exist under that model.


See Also


Authors: José Luis Minich, Maximiliano Lucius.