Skip to main content
Export your Prem API key as API_KEY before running any script. Set API_BASE_URL (defaults to https://localhost).
1

Create a project

const { project_id } = await api('/api/v1/public/projects/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Test Project', goal: 'Test finetuning' })
});
Start by creating a project workspace. The project_id is required for all subsequent operations.
2

Upload JSONL dataset

const formData = new FormData();
formData.append('project_id', project_id);
formData.append('name', 'Test Dataset');
const jsonlFile = file('sample_data.jsonl');
formData.append('file', jsonlFile, 'sample_data.jsonl');

const { dataset_id } = await api('/api/v1/public/datasets/create-from-jsonl', {
  method: 'POST',
  body: formData
});
Upload your pre-formatted JSONL file. Each line should contain a chat conversation in the expected format.
3

Wait for dataset processing

let dataset;
do {
  await sleep(2000);
  dataset = await api(`/api/v1/public/datasets/${dataset_id}`);
} while (dataset.status === 'processing');
Poll the dataset status until processing completes. JSONL uploads are typically fast.
4

Create snapshot

const { snapshot_id } = await api('/api/v1/public/snapshots/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ dataset_id, split_percentage: 80 })
});
Split the dataset into training and validation sets. The default 80/20 split works well for most cases.
5

Generate recommendations

await api('/api/v1/public/recommendations/generate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ snapshot_id, reasoning: false })
});

let recs;
do {
  await sleep(5000);
  recs = await api(`/api/v1/public/recommendations/${snapshot_id}?reasoning=false`);
} while (recs.status === 'processing');
Request model recommendations for your snapshot. Poll until recommendations are ready, then filter for recommended models.
6

Create fine-tuning job

const experiments = recs.recommended_models
  .filter((m: any) => m.recommended)
  .map((m: any) => ({
    base_model_id: m.baseModelId,
    batch_size: m.lora_hyperparameters.batchSize,
    learning_rate_multiplier: m.lora_hyperparameters.learningRateMultiplier,
    n_epochs: m.lora_hyperparameters.nEpochs,
    lora: true
  }));

const { job_id } = await api('/api/v1/public/finetuning/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ snapshot_id, name: 'Test Job', reasoning: false, experiments })
});
Build experiments from recommended models using LoRA hyperparameters, then launch the fine-tuning job.
7

Monitor job progress

for (let i = 0; i < 30; i++) {
  await sleep(10000);
  const job = await api(`/api/v1/public/finetuning/${job_id}`);
  console.log(`Status: ${job.status}`);
  job.experiments.forEach((e: any) => {
    console.log(`  - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
  });
  if (job.status !== 'processing') break;
}
Poll the job status every 10 seconds. Each experiment shows its status and final model ID when complete.

Full Example

#!/usr/bin/env bun

/**
 * Example 1: JSONL dataset workflow
 * 1. Create project → 2. Upload JSONL → 3. Create snapshot → 4. Get recommendations → 5. Run finetuning
 */

import { file } from 'bun';

const API_BASE_URL = process.env.API_BASE_URL || 'https://localhost';
const API_KEY = process.env.API_KEY;

// Disable TLS verification for local development with self-signed certs
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

if (!API_KEY) {
	console.error('Error: API_KEY environment variable is required');
	console.error('Please create a .env file based on .env.example');
	process.exit(1);
}

async function api(endpoint: string, options: any = {}): Promise<any> {
	const res = await fetch(`${API_BASE_URL}${endpoint}`, {
		...options,
		headers: { Authorization: `Bearer ${API_KEY}`, ...options.headers },
	});
	if (!res.ok) {
		const err: any = await res.json().catch(() => ({}));
		const errorMsg = typeof err.error === 'string' ? err.error : JSON.stringify(err);
		throw new Error(`${res.status}: ${errorMsg}`);
	}
	return res.json();
}

function sleep(ms: number) {
	return new Promise((r) => setTimeout(r, ms));
}

async function main() {
	console.log('\n=== JSONL Workflow ===\n');

	// 1. Create project
	console.log('1. Creating project...');
	const { project_id } = await api('/api/v1/public/projects/create', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ name: 'Test Project', goal: 'Test finetuning' }),
	});
	console.log(`   ✓ Project: ${project_id}\n`);

	// 2. Upload JSONL
	console.log('2. Uploading JSONL dataset...');
	const formData = new FormData();
	formData.append('project_id', project_id);
	formData.append('name', 'Test Dataset');
	const jsonlFile = file('sample_data.jsonl');
	formData.append('file', jsonlFile, 'sample_data.jsonl');

	const { dataset_id } = await api('/api/v1/public/datasets/create-from-jsonl', {
		method: 'POST',
		body: formData,
	});
	console.log(`   ✓ Dataset: ${dataset_id}`);

	// Wait for dataset
	console.log('   Waiting for dataset...');
	let dataset;
	do {
		await sleep(2000);
		dataset = await api(`/api/v1/public/datasets/${dataset_id}`);
	} while (dataset.status === 'processing');
	console.log(`   ✓ Ready: ${dataset.datapoints_count} datapoints\n`);

	// 3. Create snapshot
	console.log('3. Creating snapshot...');
	const { snapshot_id } = await api('/api/v1/public/snapshots/create', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ dataset_id, split_percentage: 80 }),
	});
	console.log(`   ✓ Snapshot: ${snapshot_id}\n`);

	// 4. Generate recommendations
	console.log('4. Generating recommendations...');
	await api('/api/v1/public/recommendations/generate', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ snapshot_id, reasoning: false }),
	});

	let recs;
	do {
		await sleep(5000);
		recs = await api(`/api/v1/public/recommendations/${snapshot_id}?reasoning=false`);
	} while (recs.status === 'processing');

	console.log(`   ✓ Recommended models:`);
	const recommendedCount = recs.recommended_models.filter((m: any) => m.recommended).length;
	console.log(`   Total models: ${recs.recommended_models.length}, Recommended: ${recommendedCount}`);
	recs.recommended_models.forEach((m: any) => {
		if (m.recommended) console.log(`     - ${m.baseModelId}`);
	});
	console.log();

	// 5. Create finetuning job
	console.log('5. Creating finetuning job...');
	const experiments = recs.recommended_models
		.filter((m: any) => m.recommended)
		.map((m: any) => ({
			base_model_id: m.baseModelId,
			batch_size: m.lora_hyperparameters.batchSize,
			learning_rate_multiplier: m.lora_hyperparameters.learningRateMultiplier,
			n_epochs: m.lora_hyperparameters.nEpochs,
			lora: true,
		}));

	if (experiments.length === 0) {
		console.error('\n✗ Error: No recommended models found. Cannot create finetuning job.');
		process.exit(1);
	}

	const { job_id } = await api('/api/v1/public/finetuning/create', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({ snapshot_id, name: 'Test Job', reasoning: false, experiments }),
	});
	console.log(`   ✓ Job: ${job_id}\n`);

	// 6. Monitor (5 minutes max)
	console.log('6. Monitoring job...');
	for (let i = 0; i < 30; i++) {
		await sleep(10000);
		const job = await api(`/api/v1/public/finetuning/${job_id}`);
		console.log(`   Status: ${job.status}`);
		job.experiments.forEach((e: any) => {
			console.log(`     - Exp #${e.experiment_number}: ${e.status} ${e.model_id || ''}`);
		});
		if (job.status !== 'processing') break;
	}

	console.log('\n✓ Done!\n');
}

main().catch((err) => {
	console.error('\n✗ Error:', err.message);
	process.exit(1);
});