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

# Fine-tune a model

> Upload fine-tuning data, create a fine-tuning job, monitor training progress, and use the resulting model with the `@meetkai/mka1` SDK.

Use fine-tuning when you want to adapt a base model to your own training examples and operational style.
The fine-tuning endpoints create an asynchronous job from uploaded JSONL files and return a model ID when training succeeds.

Fine-tuning endpoints are marked **ADMIN ONLY** in the generated API reference.
Use an API key with the required permissions.

## Before you start

Prepare:

| Input           | Description                                                                      |
| --------------- | -------------------------------------------------------------------------------- |
| Training file   | A JSONL file with your training examples. Upload it with `purpose: "fine-tune"`. |
| Validation file | Optional JSONL validation data. Upload it with `purpose: "fine-tune"`.           |
| Base model      | The model ID you want to fine-tune, for example `meetkai:functionary-medium`.    |

Fine-tuning jobs can return these statuses:

```
validating_files -> queued -> running -> succeeded
                          \-> failed
                          \-> cancelled
```

## Step 1 - Upload your training files

Upload each JSONL file with the Files API and `purpose: "fine-tune"`.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm files upload \
    --file ./fine-tuning-train.jsonl \
    --purpose fine-tune \
    -H 'X-On-Behalf-Of: <end-user-id>'

  mka1 llm files upload \
    --file ./fine-tuning-validation.jsonl \
    --purpose fine-tune
  ```

  ```ts MKA1 SDK theme={null}
  import { SDK } from '@meetkai/mka1';

  const mka1 = new SDK({
    bearerAuth: 'Bearer <mka1-api-key>',
  });

  const requestOptions = {
    headers: {
      'X-On-Behalf-Of': '<end-user-id>',
    },
  };

  const trainingFile = await mka1.llm.files.upload(
    {
      file: Bun.file('./fine-tuning-train.jsonl'),
      purpose: 'fine-tune',
    },
    requestOptions
  );

  const validationFile = await mka1.llm.files.upload(
    {
      file: Bun.file('./fine-tuning-validation.jsonl'),
      purpose: 'fine-tune',
    },
    requestOptions
  );

  console.log(trainingFile.id);
  console.log(validationFile.id);
  ```

  ```csharp C# SDK theme={null}
  using System.Text;
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;
  using MeetKai.MKA1.Types.Requests;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  var trainingFile = await sdk.Llm.Files.UploadAsync(new UploadFileRequestBody()
  {
      File = new UploadFileFile()
      {
          FileName = "fine-tuning-train.jsonl",
          Content = File.ReadAllBytes("./fine-tuning-train.jsonl"),
      },
      Purpose = UploadFilePurpose.FineTune,
  });

  Console.WriteLine(trainingFile.File!.Id);
  ```

  ```python Python SDK theme={null}
  from mka1 import SDK

  sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")

  training_file = sdk.llm.files.upload(
      file={"file_name": "fine-tuning-train.jsonl", "content": open("./fine-tuning-train.jsonl", "rb")},
      purpose="fine-tune",
  )

  validation_file = sdk.llm.files.upload(
      file={"file_name": "fine-tuning-validation.jsonl", "content": open("./fine-tuning-validation.jsonl", "rb")},
      purpose="fine-tune",
  )

  print(training_file.id)
  print(validation_file.id)
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/llm/files \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --form 'file=@./fine-tuning-train.jsonl;type=application/jsonl' \
    --form 'purpose=fine-tune'
  ```
</CodeGroup>

Store the returned file IDs.
You pass them to the Fine-Tuning API in the next step.

## Step 2 - Create a fine-tuning job

Call `mka1.llm.fineTuning.create` with the base model and your uploaded training file ID.
Add a validation file, suffix, metadata, and method settings when you need them.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm fine-tuning create --body '{
    "model": "meetkai:functionary-medium",
    "training_file": "file_abc123",
    "validation_file": "file_def456",
    "suffix": "support-bot",
    "seed": 42,
    "method": {
      "type": "supervised",
      "supervised": {
        "hyperparameters": {
          "n_epochs": 3
        }
      }
    },
    "metadata": {
      "experiment": "support-bot-v1"
    }
  }'
  ```

  ```ts MKA1 SDK theme={null}
  const job = await mka1.llm.fineTuning.create(
    {
      model: 'meetkai:functionary-medium',
      trainingFile: trainingFile.id,
      validationFile: validationFile.id,
      suffix: 'support-bot',
      seed: 42,
      method: {
        type: 'supervised',
        supervised: {
          hyperparameters: {
            nEpochs: 3,
          },
        },
      },
      metadata: {
        experiment: 'support-bot-v1',
      },
    },
    requestOptions
  );

  console.log(job.id);            // "ftjob_aa87e2b1112a455b8deabed784372198"
  console.log(job.status);        // "validating_files" | "queued" | "running" | ...
  console.log(job.fineTunedModel); // null until the job succeeds
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  var job = await sdk.Llm.FineTuning.CreateAsync(new CreateFineTuningJobRequest()
  {
      Model = "meetkai:functionary-medium",
      TrainingFile = trainingFile.File!.Id,
      Suffix = "support-bot",
      Seed = 42,
      Method = new Method()
      {
          Type = MethodType.Supervised,
          Supervised = new Supervised()
          {
              Hyperparameters = new SupervisedHyperparameters()
              {
                  NEpochs = SupervisedHyperparametersNEpochs.CreateInteger(3),
              },
          },
      },
  });

  Console.WriteLine(job.FineTuningJob!.Id);            // "ftjob_aa87e2b1112a455b8deabed784372198"
  Console.WriteLine(job.FineTuningJob!.Status);        // "validating_files" | "queued" | "running" | ...
  ```

  ```python Python SDK theme={null}
  job = sdk.llm.fine_tuning.create(
      model="meetkai:functionary-medium",
      training_file=training_file.id,
      validation_file=validation_file.id,
      suffix="support-bot",
      seed=42,
      method={
          "type": "supervised",
          "supervised": {
              "hyperparameters": {
                  "n_epochs": 3,
              },
          },
      },
      metadata={"experiment": "support-bot-v1"},
  )

  print(job.id)                # "ftjob_aa87e2b1112a455b8deabed784372198"
  print(job.status)            # "validating_files" | "queued" | "running" | ...
  print(job.fine_tuned_model)  # None until the job succeeds
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data '{
      "model": "meetkai:functionary-medium",
      "training_file": "file_abc123",
      "validation_file": "file_def456",
      "suffix": "support-bot",
      "seed": 42,
      "method": {
        "type": "supervised",
        "supervised": {
          "hyperparameters": {
            "n_epochs": 3
          }
        }
      },
      "metadata": {
        "experiment": "support-bot-v1"
      }
    }'
  ```
</CodeGroup>

## Step 3 - Poll job status

Retrieve the job until it reaches `succeeded`, `failed`, or `cancelled`.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm fine-tuning retrieve \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198
  ```

  ```ts MKA1 SDK theme={null}
  async function waitForFineTuningJob(
    fineTuningJobId: string,
    timeoutMs = 30 * 60_000
  ) {
    const terminalStatuses = new Set(['succeeded', 'failed', 'cancelled']);
    const start = Date.now();

    while (Date.now() - start < timeoutMs) {
      const current = await mka1.llm.fineTuning.retrieve(
        { fineTuningJobId },
        requestOptions
      );

      if (terminalStatuses.has(current.status)) {
        return current;
      }

      await new Promise((resolve) => setTimeout(resolve, 10_000));
    }

    throw new Error(`Fine-tuning job ${fineTuningJobId} did not finish in time`);
  }

  const completedJob = await waitForFineTuningJob(job.id);

  if (completedJob.status === 'succeeded') {
    console.log(completedJob.fineTunedModel);
  } else {
    console.log(completedJob.error);
  }
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  var retrieved = await sdk.Llm.FineTuning.RetrieveAsync(jobId);
  Console.WriteLine(retrieved.FineTuningJob!.Status);
  ```

  ```python Python SDK theme={null}
  import time

  def wait_for_fine_tuning_job(sdk, job_id, timeout_ms=30 * 60_000):
      terminal = {"succeeded", "failed", "cancelled"}
      start = time.time() * 1000

      while (time.time() * 1000) - start < timeout_ms:
          current = sdk.llm.fine_tuning.retrieve(fine_tuning_job_id=job_id)
          if current.status in terminal:
              return current
          time.sleep(10)

      raise TimeoutError(f"Fine-tuning job {job_id} did not finish in time")

  completed_job = wait_for_fine_tuning_job(sdk, job.id)

  if completed_job.status == "succeeded":
      print(completed_job.fine_tuned_model)
  else:
      print(completed_job.error)
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198 \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

You can also page through all jobs with `mka1.llm.fineTuning.list({ limit, after })`.

## Step 4 - Inspect training events and checkpoints

Use events for training logs and metrics updates.
Use checkpoints to inspect intermediate model checkpoints and their metrics.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm fine-tuning list-events \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198 \
    --limit 20

  mka1 llm fine-tuning list-checkpoints \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198 \
    --limit 10
  ```

  ```ts MKA1 SDK theme={null}
  const events = await mka1.llm.fineTuning.listEvents(
    {
      fineTuningJobId: job.id,
      limit: 20,
    },
    requestOptions
  );

  for (const event of events.data) {
    console.log(event.createdAt, event.level, event.message, event.data);
  }

  const checkpoints = await mka1.llm.fineTuning.listCheckpoints(
    {
      fineTuningJobId: job.id,
      limit: 10,
    },
    requestOptions
  );

  for (const checkpoint of checkpoints.data) {
    console.log(
      checkpoint.stepNumber,
      checkpoint.fineTunedModelCheckpoint,
      checkpoint.metrics
    );
  }
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  var events = await sdk.Llm.FineTuning.ListEventsAsync(
      fineTuningJobId: jobId, limit: 20);

  var checkpoints = await sdk.Llm.FineTuning.ListCheckpointsAsync(
      fineTuningJobId: jobId, limit: 10);
  ```

  ```python Python SDK theme={null}
  events = sdk.llm.fine_tuning.list_events(
      fine_tuning_job_id=job.id,
      limit=20,
  )

  for event in events.data:
      print(event.created_at, event.level, event.message, event.data)

  checkpoints = sdk.llm.fine_tuning.list_checkpoints(
      fine_tuning_job_id=job.id,
      limit=10,
  )

  for checkpoint in checkpoints.data:
      print(checkpoint.step_number, checkpoint.fine_tuned_model_checkpoint, checkpoint.metrics)
  ```

  ```bash curl theme={null}
  curl "https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198/events?limit=20" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'

  curl "https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198/checkpoints?limit=10" \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

Checkpoint metrics can include `train_loss`, `train_mean_token_accuracy`, `valid_loss`, `valid_mean_token_accuracy`, `full_valid_loss`, and `full_valid_mean_token_accuracy`.

## Step 5 - Pause, resume, or cancel a job

Use `pause` when you need to temporarily stop a running job.
Use `resume` to continue it.
Use `cancel` to stop it permanently.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm fine-tuning pause \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198

  mka1 llm fine-tuning resume \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198

  mka1 llm fine-tuning cancel \
    --fine-tuning-job-id ftjob_aa87e2b1112a455b8deabed784372198
  ```

  ```ts MKA1 SDK theme={null}
  await mka1.llm.fineTuning.pause(
    { fineTuningJobId: job.id },
    requestOptions
  );

  await mka1.llm.fineTuning.resume(
    { fineTuningJobId: job.id },
    requestOptions
  );

  await mka1.llm.fineTuning.cancel(
    { fineTuningJobId: job.id },
    requestOptions
  );
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  // Pause
  await sdk.Llm.FineTuning.PauseAsync(jobId);

  // Resume
  await sdk.Llm.FineTuning.ResumeAsync(jobId);

  // Cancel
  var cancelled = await sdk.Llm.FineTuning.CancelAsync(jobId);
  Console.WriteLine(cancelled.FineTuningJob!.Status);
  ```

  ```python Python SDK theme={null}
  # Pause
  sdk.llm.fine_tuning.pause(fine_tuning_job_id=job.id)

  # Resume
  sdk.llm.fine_tuning.resume(fine_tuning_job_id=job.id)

  # Cancel
  sdk.llm.fine_tuning.cancel(fine_tuning_job_id=job.id)
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198/pause \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'

  curl https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198/resume \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'

  curl https://apigw.mka1.com/api/v1/llm/fine_tuning/jobs/ftjob_aa87e2b1112a455b8deabed784372198/cancel \
    --request POST \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>'
  ```
</CodeGroup>

## Step 6 - Use the fine-tuned model

When the job reaches `succeeded`, `job.fineTunedModel` contains the new model ID.
Pass that model ID to a Responses request.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm responses create \
    --model ft:meetkai:functionary-medium:support-bot \
    --input '"Write a support reply for a delayed shipment."'
  ```

  ```ts MKA1 SDK theme={null}
  const response = await mka1.llm.responses.create(
    {
      model: completedJob.fineTunedModel!,
      input: 'Write a support reply for a delayed shipment.',
    },
    requestOptions
  );

  console.log(response.outputText);
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(bearerAuth: "Bearer YOUR_API_KEY");

  var response = await sdk.Llm.Responses.CreateAsync(new ResponsesCreateRequest()
  {
      Model = completedJob.FineTuningJob!.FineTunedModel!,
      Input = ResponsesCreateRequestInput.CreateStr(
          "Write a support reply for a delayed shipment."),
  });
  ```

  ```python Python SDK theme={null}
  response = sdk.llm.responses.create(
      model=completed_job.fine_tuned_model,
      input="Write a support reply for a delayed shipment.",
  )

  print(response.output_text)
  ```

  ```bash curl theme={null}
  curl https://apigw.mka1.com/api/v1/llm/responses \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer <mka1-api-key>' \
    --header 'X-On-Behalf-Of: <end-user-id>' \
    --data '{
      "model": "ft:meetkai:functionary-medium:support-bot",
      "input": "Write a support reply for a delayed shipment."
    }'
  ```
</CodeGroup>

## API reference

For the full request and response schema, open the Fine-Tuning group in the [API Reference](/api-reference/introduction).
