src/native.ps1
# # Copyright (c), Adam Edwards # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # So it turns out that certain native libraries are a real pain, at least if you # want to package libraries for multiple platforms (e.g. both x64 and arm64 on Windows) # within the same software package like this one. The *onnx* packages used for local models # include native libraries that are placed within platform specific directories invisible # to the assembly loader, and thus they don't get loaded (and fail the loading of the # parent assembly). To work around this, we check for their presence in the directory # with all platform neutral assemblies when this script is executed at module load. If # they are not present we copy the assemblies from the correct platform-specific directory # into the platform neutral directory so that the assembly loader will find them when it # attempst to load the parent assemblies. # set-strictmode -version 2 $erroractionpreference = 'stop' $proxyExecutablePath = "$psscriptroot/../lib/AIProxy.exe" function CurrentScriptDirectory { split-path -parent $psscriptroot } function GetCurrentPlatformIdentifier { $osFragment = [OperatingSystem]::IsLinux() ? "linux" : ( [OperatingSystem]::IsMacOS() ? "osx" : ( [OperatingSystem]::IsWindows() ? "win" : $null ) ) $archFragment = if ( $osFragment ) { switch ( [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture ) { ([System.Runtime.InteropServices.Architecture]::X64) { "x64" } ([System.Runtime.InteropServices.Architecture]::Arm64) { "arm64" } } } if ( $archFragment ) { "$($osFragment)-$($archFragment)" } } function GetNativeLibrarySourceDirectory { $osFragment = [OperatingSystem]::IsLinux() ? "linux" : ( [OperatingSystem]::IsMacOS() ? "osx" : ( [OperatingSystem]::IsWindows() ? "win" : $null ) ) $archFragment = if ( $osFragment ) { switch ( [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture ) { ([System.Runtime.InteropServices.Architecture]::X64) { "x64" } ([System.Runtime.InteropServices.Architecture]::Arm64) { "arm64" } } } $currentNativePlatformIdentifier = GetCurrentPlatformIdentifier if ( $currentNativePlatformIdentifier ) { $thisDirectory = CurrentScriptDirectory $targetPlatformSubdirectory = Join-Path 'lib/runtimes' $currentNativePlatformIdentifier $targetSubdirectory = Join-Path $targetPlatformSubdirectory native Join-Path $thisDirectory $targetSubdirectory } } function ConfigureNativeLibraries([bool] $skipCopy = $false, [string] $warningActionValue = 'Continue') { $nativeLibrarySource = GetNativeLibrarySourceDirectory $nativeLibraryDestination = join-path (CurrentScriptDirectory) lib if ( ! $nativeLibrarySource ) { write-warning "Unable to determine native library source directory -- native operations may not be supported for the current platform" -warningaction $warningActionValue } elseif ( ! ( test-path $nativeLibrarySource ) ) { write-warning "Native library source directory '$nativeLibrarySource' does not exist -- native operations will not be supported" -warningaction $warningActionValue } else { if ( test-path $nativeLibraryDestination ) { $nativeLibraries = if ( [OperatingSystem]::IsLinux() ) { } elseif ( [OperatingSystem]::IsLinux() ) { @( 'libonnxruntime_providers_shared.so' 'libonnxruntime-genai.so' 'libonnxruntime.so') } elseif ( [OperatingSystem]::IsMacOS() ) { @( 'libonnxruntime-genai.dylib' 'libonnxruntime.dylib') } elseif ( [OperatingSystem]::IsWindows() ) { @( 'onnxruntime_providers_shared.dll' 'onnxruntime-genai.dll' 'onnxruntime.dll') } foreach ( $library in $nativeLibraries ) { $sourceLibraryPath = join-path $nativeLibrarySource $library $targetLibraryPath = join-path $nativeLibraryDestination $library if ( ! ( test-path $targetLibraryPath ) ) { if ( test-path $sourceLibraryPath ) { if ( ! $skipCopy ) { copy-item $sourceLibraryPath $targetLibraryPath } else { if ( ! ( test-path $sourceLibraryPath ) ) { throw 'Native library not not found' } } } else { write-warning "Unable to find native library '$sourceLibraryPath'; native operations may not be supported for the current platform" -warningaction $warningActionValue } } } } else { write-warning "Cannot find module library dependency directory '$nativeLibraryDestination'; the module may not function." -warningaction $warningActionValue } } } function ConfigureNativeExecutables { if ( $PSVersionTable.Platform -ne 'Win32NT' ) { write-verbose 'Starting native executable configuration because the platform is not Windows' $script:proxyExecutableInfo = Get-Item $script:proxyExecutablePath -ErrorAction Ignore if ( $proxyExecutableInfo ) { $hasExecutableMode = $proxyExecutableInfo.UnixFileMode -band [System.IO.UnixFileMode]::UserExecute if ( ! $hasExecutableMode ) { if ( get-command chmod -erroraction ignore ) { & chmod +x $script:proxyExecutablePath if ( $LASTEXITCODE -ne 0 ) { write-warning "Unable to set executable mode on the path '$script:proxyExecutablePath' on a non-Windows platform; AI operations will likely fail." } else { write-verbose "Successfully updated the executable attribute of the proxy executable at '$script:proxyExecutablePath'" } } else { write-warning "Unable to find the 'chmod' command to update the executable mode of the '$script:proxyExecutablePath' file on a non-Windows platform; AI operations will likely fail." } } else { write-verbose "Proxy executable file at path '$script:proxyExecutablePath' has the executable attribute set, no changes needed." } } else { write-warning "Unable to locate the path '$script:proxyExecutablePath' to set executable mode on non-Windows platform; AI operations may fail." } } else { write-verbose 'Skipping native executable configuration because it is not needed on the Windows OS platform.' } } $skipVariable = get-variable -scope global __ChatGPSSkipNative -erroraction ignore if ( ! $skipVariable -or ! ( $skipVariable.value -eq $true ) ) { write-verbose "Starting native configuration in runtime mode." ConfigureNativeExecutables ConfigureNativeLibraries -warningactionvalue SilentlyContinue } else { write-verbose "Skipping native configuration because this is not runtime mode." ConfigureNativeLibraries $true -warningactionvalue SilentlyContinue } |