public/New-TuneModel.ps1

function New-TuneModel {
    <#
    .SYNOPSIS
    Creates a new fine-tuned model.
 
    .DESCRIPTION
    New-TuneModel initiates the process of creating a new fine-tuned model based on a specified base model and training data.
 
    .PARAMETER FilePath
    The path to the training file (.jsonl) used for fine-tuning.
 
    .PARAMETER Model
    The base model to use for fine-tuning. Can be one of the predefined models or a fine-tuned model (starting with "ft:").
    Default is "gpt-3.5-turbo-0125".
 
    .PARAMETER Append
    If specified, appends all files to the same model. If not used, a new model will be created for each file.
 
    .EXAMPLE
    New-TuneModel -FilePath .\sample\data.jsonl -Model gpt-3.5-turbo-0125
 
    Creates a new fine-tuned model using the data.jsonl file and the gpt-3.5-turbo-0125 base model.
 
    .EXAMPLE
    Get-ChildItem *.jsonl | New-TuneModel -Model ft:gpt-3.5-turbo-0613:my-org:custom_model:7p4lURx -Append
 
    Creates a new fine-tuned model by appending all .jsonl files in the current directory to an existing fine-tuned model.
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateScript({ Test-Path $PSItem })]
        [System.IO.FileInfo]$FilePath,
        [Parameter(Position = 0)]
        [ArgumentCompleter({
                param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
                $script:ValidModels | Where-Object { $_ -like "$WordToComplete*" }
            })]
        [string]$Model = "gpt-3.5-turbo-0125",
        [switch]$Append
    )
    process {
        if ($PSCmdlet.ShouldProcess("Fine-tuning model $Model using training file $FilePath")) {
            # Upload the file and get the file ID
            $basename = Get-ChildItem $FilePath | Select-Object -ExpandProperty BaseName
            $fileId = Send-TuneFile -FilePath $FilePath | Select-Object -ExpandProperty Id
            Write-Verbose "Uploaded $FilePath with ID $fileId"

            # Start the tune job
            Write-Verbose "Starting fine-tuning job for model $Model with FileId $fileId"
            $tuneJobId = Start-TuneJob -Id $fileId -Model $Model | Select-Object -ExpandProperty Id
            Write-Verbose "Started fine-tuning job $tuneJobId"

            # Initialize the progress bar
            $progress = @{
                Status          = "Waiting for fine-tuning for $basename to complete"
                Activity        = "Fine-tuning $Model"
                PercentComplete = 0
            }
            Write-Progress @progress

            $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

            # Check the status every 5 seconds
            do {
                $jobStatus = Get-TuneJob -Id $tuneJobId
                Write-Verbose "Got info for tune job $tuneJobId"

                # Update progress bar
                if ($true) {
                    $progress.PercentComplete = ($progress.PercentComplete) + 1
                }
                Write-Progress @progress

                if ($jobStatus.status -in "failed", "cancelled") {
                    <# statuses: running, succeeded, failed, cancelled #>
                    $tunejob = Get-TuneJob -Id $tuneJobId

                    throw "Fine-tuning job $tuneJobId for file $basename failed: $($tunejob.error.message)"
                    # not sure if it needs a throw and a break?
                    break
                }

                if ($jobStatus.status -eq "succeeded") {
                    $script:currentmodel = $Model = $jobStatus.fine_tuned_model
                    Write-Verbose "Your new model is named $script:currentmodel and will now be used by default when executing Invoke-TuneChat"
                    break
                } else {
                    Write-Verbose "Fine-tuning job $tuneJobId is still running"
                }

                # Restart progress if it gets to 100 without a fine_tuned_model value
                if ($progress.PercentComplete -ge 100) {
                    $progress.PercentComplete = 0
                }
                Start-Sleep -Seconds 15
            } while ($true)
            $null = $stopwatch.Stop()

            Write-Verbose "Tuning took $($stopwatch.Elapsed)"


            # Initialize the progress bar
            $progressparm = @{
                Status          = "Fine tuning complete! Now waiting for the new model, $script:currentmodel, to be ready.."
                Activity        = "Fine-tuning $Model"
                PercentComplete = ($progress.PercentComplete) + 1
            }
            Write-Progress @progressparm

            # Use ChatGPT to inform the user about the new model
            Start-Sleep 25
            $params = @{
                Message       = "Respond only saying: 'Say hello to your new model, $script:currentmodel'"
                Model         = $script:currentmodel
                ErrorAction   = "SilentlyContinue"
                ErrorVariable = "weberror"
            }
            Invoke-TuneChat @params
            if ($weberror) {
                Write-Warning "This happens sometimes :/ Seems like the new model needs a nap, trying again in 10 seconds"
                Start-Sleep 10

                $params = @{
                    Message     = "Respond only saying: 'Say hello to your new model, $script:currentmodel'"
                    Model         = $script:currentmodel
                    ErrorAction   = "SilentlyContinue"
                    ErrorVariable = "weberror2"
                }
                Invoke-TuneChat @params

                if ($weberror2) {
                    Write-Output "Fine, since ChatGPT won't tell you, your new model is $script:currentmodel"
                }
            }
        }
    }
}