public/New-TuneModel.ps1

function New-TuneModel {
<#
    .SYNOPSIS
        Automates the entire fine-tuning process for an OpenAI model.
 
    .DESCRIPTION
        This command uploads a training file, starts a fine-tuning job, and monitors its progress.
        Once the job is complete, it sets the new fine-tuned model as the current model and notifies the user.
 
    .PARAMETER FilePath
        The path to the training file (.jsonl) used for fine-tuning.
 
    .PARAMETER Model
        The base model to use for fine-tuning. Defaults to gpt-3.5-turbo-0613.
 
    .PARAMETER Append
        Append all files to the same model. If this switch is not used, a new model will be created for each file.
 
    .PARAMETER WhatIf
        Shows what would happen if the cmdlet runs. The cmdlet is not run.
 
    .PARAMETER Confirm
        Prompts you for confirmation before running the cmdlet.
 
    .EXAMPLE
        New-TuneModel -FilePath .\sample\potato.jsonl -Model gpt-3.5-turbo-0613
 
        Uploads the potato.jsonl file and starts a fine-tuning job using the gpt-3.5-turbo-0613 model.
 
        .EXAMPLE
        Get-ChildItem *.jsonl | New-TuneModel -Model gpt-3.5-turbo-0613 -Append
 
        Pipes all .jsonl files in the current directory to New-TuneModel, appending them to the gpt-3.5-turbo-0613 model for fine-tuning, instead of creating a new model for each file.
 
    .EXAMPLE
        New-TuneModel -FilePath .\sample\potato.jsonl -Model gpt-3.5-turbo-0613 -WhatIf
 
        Shows what would happen if the cmdlet runs, but does not execute the cmdlet.
 
    .EXAMPLE
        New-TuneModel -FilePath .\sample\potato.jsonl -Model gpt-3.5-turbo-0613 -Confirm
 
        Prompts for confirmation before executing the cmdlet.
#>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]$FilePath,
        [string]$Model = 'gpt-3.5-turbo-0613',
        [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 #>
                    throw "Fine-tuning job $tuneJobId failed"
                    # 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)"

            # Use ChatGPT to inform the user about the new model
            Start-Sleep 5
            $params = @{
                Message       = "Can you say 'Say hello to your new model, $script:currentmodel!' exactly like that?"
                Model         = $script:currentmodel
                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     = "Can you say 'Say hello to your new model, $script:currentmodel!' exactly like that?"
                    Model         = $script:currentmodel
                    ErrorVariable = "weberror2"
                }
                Invoke-TuneChat @params

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