diff --git a/.github/workflows/build-cs-steps.yml b/.github/workflows/build-cs-steps.yml index a77b9d5..20d4886 100644 --- a/.github/workflows/build-cs-steps.yml +++ b/.github/workflows/build-cs-steps.yml @@ -30,23 +30,21 @@ jobs: with: clean: true - # adapted from https://github.com/actions/setup-dotnet?tab=readme-ov-file#azure-artifacts - # saves auth credentials to ../nuget.config - name: Setup .NET 9 SDK uses: actions/setup-dotnet@v5 with: dotnet-version: '9.0.x' - source-url: https://pkgs.dev.azure.com/microsoft/windows.ai.toolkit/_packaging/Neutron/nuget/v3/index.json env: NUGET_AUTH_TOKEN: ${{ secrets.AZURE_DEVOPS_PAT }} + # /p:FoundryLocalCoreVersion="*-*" to always use nightly version of Foundry Local Core - name: Restore dependencies run: | - dotnet restore sdk_v2\cs\src\Microsoft.AI.Foundry.Local.csproj /p:UseWinML=${{ inputs.useWinML }} --configfile ../nuget.config + dotnet restore sdk_v2\cs\src\Microsoft.AI.Foundry.Local.csproj /p:UseWinML=${{ inputs.useWinML }} /p:FoundryLocalCoreVersion="*-*" --configfile sdk_v2\cs\NuGet.config - name: Build solution run: | - dotnet build sdk_v2\cs\src\Microsoft.AI.Foundry.Local.csproj --configfile ../nuget.config --no-restore --configuration ${{ inputs.buildConfiguration }} /p:UseWinML=${{ inputs.useWinML }} + dotnet build sdk_v2\cs\src\Microsoft.AI.Foundry.Local.csproj --no-restore --configuration ${{ inputs.buildConfiguration }} /p:UseWinML=${{ inputs.useWinML }} /p:FoundryLocalCoreVersion="*-*" # need to use direct git commands to clone from Azure DevOps instead of actions/checkout - name: Checkout test-data-shared from Azure DevOps @@ -80,7 +78,7 @@ jobs: - name: Run Foundry Local Core tests run: | - dotnet test sdk_v2\cs\test\FoundryLocal.Tests\Microsoft.AI.Foundry.Local.Tests.csproj --verbosity normal /p:UseWinML=${{ inputs.useWinML }} + dotnet test sdk_v2\cs\test\FoundryLocal.Tests\Microsoft.AI.Foundry.Local.Tests.csproj --verbosity normal /p:UseWinML=${{ inputs.useWinML }} /p:FoundryLocalCoreVersion="*-*" - name: Pack NuGet package shell: pwsh @@ -90,14 +88,21 @@ jobs: $version = "${{ inputs.version }}" $config = "${{ inputs.buildConfiguration }}" $useWinML = "${{ inputs.useWinML }}" + # $coreVersion = "${{ env.FOUNDRY_CORE_VERSION }}" + + # Always mark as prerelease since we use nightly core + if (-not $version.Contains("dev")) { + $version = "$version-dev" + } Write-Host "Packing project: $projectPath" Write-Host "Output directory: $outputDir" Write-Host "Version: $version" Write-Host "Configuration: $config" Write-Host "UseWinML: $useWinML" + # Write-Host "FoundryLocalCoreVersion: $coreVersion" - & dotnet pack $projectPath --no-build --configuration $config --output $outputDir /p:PackageVersion=$version /p:UseWinML=$useWinML /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg --verbosity normal + & dotnet pack $projectPath --no-build --configuration $config --output $outputDir /p:PackageVersion=$version /p:UseWinML=$useWinML /p:FoundryLocalCoreVersion="*-*" /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg --verbosity normal if ($LASTEXITCODE -ne 0) { Write-Error "dotnet pack failed with exit code $LASTEXITCODE" diff --git a/.github/workflows/build-js-steps.yml b/.github/workflows/build-js-steps.yml index 37c3e06..cafd7b5 100644 --- a/.github/workflows/build-js-steps.yml +++ b/.github/workflows/build-js-steps.yml @@ -34,7 +34,6 @@ jobs: uses: actions/setup-dotnet@v5 with: dotnet-version: '9.0.x' - source-url: https://pkgs.dev.azure.com/microsoft/windows.ai.toolkit/_packaging/Neutron/nuget/v3/index.json env: NUGET_AUTH_TOKEN: ${{ secrets.AZURE_DEVOPS_PAT }} @@ -89,7 +88,7 @@ jobs: - name: npm install (Standard) if: ${{ inputs.useWinML == false }} working-directory: sdk_v2\js - run: npm install + run: npm install --nightly # Verify that installing new packages doesn't strip custom native binary folders - name: npm install openai (verify persistence) diff --git a/sdk_v2/cs/NuGet.config b/sdk_v2/cs/NuGet.config new file mode 100644 index 0000000..cf09cc0 --- /dev/null +++ b/sdk_v2/cs/NuGet.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj b/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj index c292ef8..979994a 100644 --- a/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj +++ b/sdk_v2/cs/src/Microsoft.AI.Foundry.Local.csproj @@ -59,6 +59,7 @@ + @@ -67,6 +68,13 @@ + + + + + + @@ -87,6 +95,12 @@ $(NoWarn);CsWinRT1028 + + + + 0.8.1.0 + + True @@ -96,9 +110,9 @@ + Include="Microsoft.AI.Foundry.Local.Core.WinML" Version="$(FoundryLocalCoreVersion)" /> + Include="Microsoft.AI.Foundry.Local.Core" Version="$(FoundryLocalCoreVersion)" /> diff --git a/sdk_v2/js/script/install.cjs b/sdk_v2/js/script/install.cjs index 2208161..d175798 100644 --- a/sdk_v2/js/script/install.cjs +++ b/sdk_v2/js/script/install.cjs @@ -39,28 +39,35 @@ const REQUIRED_FILES = [ // When you run npm install --winml, npm does not pass --winml as a command-line argument to your script. // Instead, it sets an environment variable named npm_config_winml to 'true'. const useWinML = process.env.npm_config_winml === 'true'; +const useNightly = process.env.npm_config_nightly === 'true'; console.log(`[foundry-local] WinML enabled: ${useWinML}`); +console.log(`[foundry-local] Nightly enabled: ${useNightly}`); const NUGET_FEED = 'https://api.nuget.org/v3/index.json'; const ORT_FEED = 'https://pkgs.dev.azure.com/aiinfra/PublicPackages/_packaging/ORT/nuget/v3/index.json'; +const ORT_NIGHTLY_FEED = 'https://pkgs.dev.azure.com/aiinfra/PublicPackages/_packaging/ORT-Nightly/nuget/v3/index.json'; + +// If nightly is requested, pull Core/GenAI from the ORT-Nightly feed where nightly builds are published. +// Otherwise use the standard NuGet.org feed. +const CORE_FEED = useNightly ? ORT_NIGHTLY_FEED : NUGET_FEED; const ARTIFACTS = [ { name: useWinML ? 'Microsoft.AI.Foundry.Local.Core.WinML' : 'Microsoft.AI.Foundry.Local.Core', - version: '0.8.2.2', + version: useNightly ? undefined : '0.8.2.2', // Set later using resolveLatestVersion if undefined files: ['Microsoft.AI.Foundry.Local.Core'], - feed: NUGET_FEED + feed: CORE_FEED }, { name: 'Microsoft.ML.OnnxRuntime.Foundry', - version: '1.23.2', + version: '1.23.2.1', // Hardcoded stable version files: ['onnxruntime'], - feed: ORT_FEED + feed: ORT_NIGHTLY_FEED }, { name: useWinML ? 'Microsoft.ML.OnnxRuntimeGenAI.WinML' : 'Microsoft.ML.OnnxRuntimeGenAI.Foundry', - version: '0.11.4', + version: '0.11.2', // Hardcoded stable version files: ['onnxruntime-genai'], feed: NUGET_FEED } @@ -68,8 +75,13 @@ const ARTIFACTS = [ // Check if already installed if (fs.existsSync(BIN_DIR) && REQUIRED_FILES.every(f => fs.existsSync(path.join(BIN_DIR, f)))) { - console.log(`[foundry-local] Native libraries already installed.`); - process.exit(0); + if (useNightly) { + console.log(`[foundry-local] Nightly requested. Forcing reinstall...`); + fs.rmSync(BIN_DIR, { recursive: true, force: true }); + } else { + console.log(`[foundry-local] Native libraries already installed.`); + process.exit(0); + } } console.log(`[foundry-local] Installing native libraries for ${RID}...`); @@ -143,7 +155,7 @@ async function downloadFile(url, dest) { // Map to cache service index resources const serviceIndexCache = new Map(); -async function resolvePackageRawUrl(feedUrl, packageName, version) { +async function getBaseAddress(feedUrl) { // 1. Get Service Index if (!serviceIndexCache.has(feedUrl)) { const index = await downloadJson(feedUrl); @@ -162,7 +174,36 @@ async function resolvePackageRawUrl(feedUrl, packageName, version) { const baseAddress = baseAddressRes['@id']; // Ensure trailing slash - const properBase = baseAddress.endsWith('/') ? baseAddress : baseAddress + '/'; + return baseAddress.endsWith('/') ? baseAddress : baseAddress + '/'; +} + +async function resolveLatestVersion(feedUrl, packageName) { + const baseAddress = await getBaseAddress(feedUrl); + const nameLower = packageName.toLowerCase(); + + // Fetch version list: {baseAddress}/{lower_id}/index.json + const versionsUrl = `${baseAddress}${nameLower}/index.json`; + try { + const versionData = await downloadJson(versionsUrl); + const versions = versionData.versions || []; + + if (versions.length === 0) { + throw new Error('No versions found'); + } + + // Sort descending to prioritize latest date-based versions (e.g. 0.9.0-dev.YYYYMMDD...) + versions.sort((a, b) => b.localeCompare(a)); + + const latestVersion = versions[0]; + console.log(`[foundry-local] Installing latest version of Foundry Local Core: ${latestVersion}`); + return latestVersion; + } catch (e) { + throw new Error(`Failed to fetch versions for ${packageName} from ${versionsUrl}: ${e.message}`); + } +} + +async function resolvePackageRawUrl(feedUrl, packageName, version) { + const properBase = await getBaseAddress(feedUrl); // 3. Construct .nupkg URL (lowercase is standard for V3) const nameLower = packageName.toLowerCase(); @@ -173,9 +214,15 @@ async function resolvePackageRawUrl(feedUrl, packageName, version) { async function installPackage(artifact, tempDir) { const pkgName = artifact.name; - const pkgVer = artifact.version; const feedUrl = artifact.feed; + // Resolve version if not specified + let pkgVer = artifact.version; + if (!pkgVer) { + console.log(` Resolving latest version for ${pkgName}...`); + pkgVer = await resolveLatestVersion(feedUrl, pkgName); + } + console.log(` Resolving ${pkgName} ${pkgVer}...`); const downloadUrl = await resolvePackageRawUrl(feedUrl, pkgName, pkgVer);