Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ jobs:
BUNDLE_ID="${{ env.APP_BUNDLE_ID }}"
# Android wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit)
# Timeout set to 15 minutes (900000ms) for audio transcription tests (whisper models can be slow)
WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"Android","appium:automationName":"UiAutomator2","appium:appPackage":"'${{ env.APP_BUNDLE_ID }}'","appium:appActivity":"'${{ env.APP_BUNDLE_ID }}'.MainActivity","appium:newCommandTimeout":300,"appium:autoGrantPermissions":true,"appium:autoAcceptAlerts":true,"appium:noReset":true,"appium:dontStopAppOnReset":true,"appium:forceAppLaunch":false}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n🛑 APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("android=new UiSelector().textContains(\\"INITIALIZED\\")");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("android=new UiSelector().textContains(\\"Run Automated Tests\\")");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};'
WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["tests/*.spec.js","tests/*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"Android","appium:automationName":"UiAutomator2","appium:appPackage":"'${{ env.APP_BUNDLE_ID }}'","appium:appActivity":"'${{ env.APP_BUNDLE_ID }}'.MainActivity","appium:newCommandTimeout":300,"appium:autoGrantPermissions":true,"appium:autoAcceptAlerts":true,"appium:noReset":true,"appium:dontStopAppOnReset":true,"appium:forceAppLaunch":false}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n🛑 APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("android=new UiSelector().textContains(\\"INITIALIZED\\")");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("android=new UiSelector().textContains(\\"Run Automated Tests\\")");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};'
else
PLATFORM="iOS"
AUTOMATION="XCUITest"
Expand All @@ -941,7 +941,7 @@ jobs:
# iOS wdio config with crash detection (bail:0 = continue on test failures, crash = process.exit)
# usePrebuiltWDA uses Device Farm's pre-built WebDriverAgent
# Timeout set to 15 minutes (900000ms) for audio transcription tests (whisper models can be slow)
WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["*.spec.js","*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n🛑 APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \\"INITIALIZED\\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \\"Run Automated Tests\\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};'
WDIO_CONFIG='exports.config={runner:"local",hostname:"127.0.0.1",port:4723,path:"/wd/hub",specs:["tests/*.spec.js","tests/*.test.js"],maxInstances:1,bail:0,capabilities:[{platformName:"iOS","appium:automationName":"XCUITest","appium:bundleId":"'${{ env.APP_BUNDLE_ID }}'","appium:newCommandTimeout":300,"appium:noReset":true,"appium:forceAppLaunch":false,"appium:usePrebuiltWDA":true,"appium:wdaLocalPort":8100,"appium:showIOSLog":true,"appium:realDeviceLogger":"/usr/local/lib/node_modules/appium/node_modules/deviceconsole/deviceconsole"}],logLevel:"debug",waitforTimeout:120000,connectionRetryTimeout:30000,connectionRetryCount:3,services:[],framework:"mocha",reporters:["spec"],mochaOpts:{ui:"bdd",timeout:900000},before:async function(capabilities,specs,browser){const BUNDLE_ID="'${{ env.APP_BUNDLE_ID }}'";global.appCrashed=false;global.checkAppCrash=async(stage)=>{try{const state=await browser.queryAppState(BUNDLE_ID);console.log("["+stage+"] App state: "+state+" (4=foreground,3=background,1=not running)");if(state<3){console.error("\\n🛑 APP CRASHED at "+stage+"! State="+state);console.error("Check device logs for BareKit/native errors.\\n");global.appCrashed=true;process.exit(1);}return state;}catch(e){console.log("["+stage+"] queryAppState error: "+e.message);return-1;}};console.log("Checking initial app state...");await global.checkAppCrash("startup");console.log("Waiting for app to initialize...");await browser.pause(5000);await global.checkAppCrash("after-pause");const initText=await browser.$("-ios predicate string:label CONTAINS \\"INITIALIZED\\"");await initText.waitForDisplayed({timeout:60000});await global.checkAppCrash("after-init");console.log("App initialized, clicking Run Automated Tests...");const button=await browser.$("-ios predicate string:label CONTAINS \\"Run Automated Tests\\"");await button.waitForDisplayed({timeout:15000});await button.click();console.log("Button clicked!");await browser.pause(5000);await global.checkAppCrash("after-click");},afterTest:async function(test,context,{error}){if(global.appCrashed)return;await global.checkAppCrash("after-test:"+test.title);}};'
fi

# Base64 encode the wdio config to safely embed in YAML
Expand Down
86 changes: 74 additions & 12 deletions .github/workflows/integration-test-qvac-lib-infer-whispercpp.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Integration Tests (Whispercpp)
name: Run Integration Tests

on:
workflow_dispatch:
Expand Down Expand Up @@ -46,7 +46,7 @@ jobs:
- os: macos-14-xlarge
platform: darwin
arch: arm64
- os: windows-2022
- os: ai-run-windows-gpu
platform: win32
arch: x64

Expand All @@ -68,7 +68,8 @@ jobs:
repository: ${{ inputs.repository || github.repository }}
token: ${{ secrets.PAT_TOKEN }}

- name: Configure scoped registry for @tetherto and @qvac packages
- name: Configure scoped registry for @tetherto and @qvac packages (Unix)
if: ${{ matrix.platform != 'win32' }}
working-directory: ${{ env.WORKDIR }}
env:
GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -80,6 +81,7 @@ jobs:
set -eu
echo "Writing .npmrc for dual registry install…"
cat > .npmrc <<NPMRC
always-auth=true
registry=https://registry.npmjs.org/
@qvac:registry=https://registry.npmjs.org/
@tetherto:registry=https://npm.pkg.github.com/
Expand All @@ -95,12 +97,36 @@ jobs:
git config --global url."https://${{ github.token }}:@github.com/".insteadOf "https://github.com/"
fi

- name: Configure scoped registry for @tetherto and @qvac packages (Windows)
if: ${{ matrix.platform == 'win32' }}
working-directory: ${{ env.WORKDIR }}
env:
GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_PAT: ${{ secrets.PAT_TOKEN }}
shell: powershell
run: |
Write-Host "Configuring scoped registry for @tetherto and @qvac packages..."
$npmrc = @"
always-auth=true
registry=https://registry.npmjs.org/
@qvac:registry=https://registry.npmjs.org/
@tetherto:registry=https://npm.pkg.github.com/
//registry.npmjs.org/:_authToken=$env:NPM_TOKEN
//npm.pkg.github.com/:_authToken=$env:GPR_TOKEN
"@
$npmrc | Out-File -FilePath .npmrc -Encoding utf8
if ($env:GIT_PAT) {
git config --global url."https://$($env:GIT_PAT):@github.com/".insteadOf "https://github.com/"
} else {
git config --global url."https://${{ github.token }}:@github.com/".insteadOf "https://github.com/"
}

- name: Install NPM dependencies
working-directory: ${{ env.WORKDIR }}
shell: bash
run: |
npm install
npm install -g bare bare-make
npm install -g bare@1.26.0 bare-make

- name: Download prebuilds from artifact
if: ${{ !inputs.prebuild_package }}
Expand All @@ -109,15 +135,33 @@ jobs:
path: ${{ env.WORKDIR }}/prebuilds
merge-multiple: true

- name: Download prebuilds from package
if: ${{ inputs.prebuild_package }}
- name: Download prebuilds from package (Unix)
if: ${{ inputs.prebuild_package && matrix.platform != 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: bash
run: |
mkdir -p prebuilds
npm pack "${{ inputs.prebuild_package }}" --pack-destination /tmp
npm pack ${{ inputs.prebuild_package }} --pack-destination /tmp
tar -xzf /tmp/*.tgz -C /tmp
cp -r /tmp/package/prebuilds/* prebuilds/
find prebuilds -type f -name 'tetherto__*' | while read f; do
mv "$f" "${f/tetherto__/qvac__}"
done

- name: Download prebuilds from package (Windows)
if: ${{ inputs.prebuild_package && matrix.platform == 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: powershell
run: |
New-Item -ItemType Directory -Force -Path prebuilds | Out-Null
npm pack ${{ inputs.prebuild_package }} --pack-destination $env:TEMP
$tgz = Get-ChildItem "$env:TEMP\*.tgz" | Select-Object -First 1
tar -xzf $tgz.FullName -C $env:TEMP
Copy-Item -Path "$env:TEMP\package\prebuilds\*" -Destination prebuilds -Recurse -Force
Get-ChildItem -Path prebuilds -Recurse -File -Filter 'tetherto__*' | ForEach-Object {
$newName = $_.Name -replace 'tetherto__', 'qvac__'
Rename-Item -Path $_.FullName -NewName $newName
}

- name: Install gh cli for ai-run-linux-gpu
if: matrix.os == 'ai-run-linux-gpu'
Expand Down Expand Up @@ -155,18 +199,36 @@ jobs:
run: |
brew install --quiet openblas lapack fftw

- name: Run integration test
- name: Run integration test (Unix)
if: ${{ matrix.platform != 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: bash
run: |
npm run test:integration
run: npm run test:integration
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}

- name: Print run state
- name: Run integration test (Windows)
if: ${{ matrix.platform == 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: powershell
run: npm run test:integration
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}

- name: Print run state (Unix)
if: ${{ matrix.platform != 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: bash
run: |
ls -la prebuilds || true
tree prebuilds || true
continue-on-error: true

- name: Print run state (Windows)
if: ${{ matrix.platform == 'win32' }}
working-directory: ${{ env.WORKDIR }}
shell: powershell
run: |
Get-ChildItem prebuilds -ErrorAction SilentlyContinue
tree prebuilds /F 2>$null
continue-on-error: true
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,6 @@ void WhisperModel::load() {
throw std::runtime_error("Failed to initialize Whisper context");
}

state_.reset(whisper_init_state(ctx_.get()));
if (state_ == nullptr) {
QLOG(
qvac_lib_inference_addon_cpp::logger::Priority::ERROR,
"Failed to initialize Whisper state");
throw std::runtime_error("Failed to initialize Whisper state");
}
Comment thread
freddy311082 marked this conversation as resolved.
is_loaded_ = true;
QLOG(
qvac_lib_inference_addon_cpp::logger::Priority::INFO,
Expand Down Expand Up @@ -238,9 +231,8 @@ void WhisperModel::warmup() {
params.new_segment_callback_user_data = nullptr;

// Run warmup inference to "heat up" the model
whisper_full_with_state(
whisper_full(
ctx_.get(),
state_.get(),
params,
silentAudio.data(),
static_cast<int>(silentAudio.size()));
Expand Down Expand Up @@ -282,12 +274,8 @@ void WhisperModel::process(const Input& input) {
params.new_segment_callback = onNewSegment;
params.new_segment_callback_user_data = &ud;

int result = whisper_full_with_state(
ctx_.get(),
state_.get(),
params,
input.data(),
static_cast<int>(input.size()));
int result = whisper_full(
ctx_.get(), params, input.data(), static_cast<int>(input.size()));

const auto t1 = std::chrono::steady_clock::now();
totalWallMs_ += std::chrono::duration<double, std::milli>(t1 - t0).count();
Expand Down Expand Up @@ -371,10 +359,7 @@ bool WhisperModel::configContextIsChanged(
return false;
}

void WhisperModel::resetContext() {
ctx_.reset();
state_.reset();
}
void WhisperModel::resetContext() { ctx_.reset(); }

void WhisperModel::setConfig(const WhisperConfig& config) {
bool contextChanged = configContextIsChanged(cfg_, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,7 @@ class WhisperModel {
}
};

struct WhisperStateDeleter {
void operator()(whisper_state* state) const noexcept {
if (state != nullptr) {
whisper_free_state(state);
}
}
};

std::unique_ptr<whisper_context, WhisperContextDeleter> ctx_{nullptr};
std::unique_ptr<whisper_state, WhisperStateDeleter> state_{nullptr};
Comment thread
freddy311082 marked this conversation as resolved.
bool stream_ended_ = false;
bool is_loaded_ = false;
bool is_warmed_up_ = false;
Expand Down
2 changes: 1 addition & 1 deletion packages/qvac-lib-infer-whispercpp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qvac/transcription-whispercpp",
"version": "0.3.17",
"version": "0.3.18",
"description": "transcription addon for qvac",
"addon": true,
"engines": {
Expand Down
25 changes: 25 additions & 0 deletions packages/qvac-lib-infer-whispercpp/release-notes/v0.3.18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# QVAC Transcription Whisper Addon v0.3.18 Release Notes

This release updates the whisper.cpp integration to use the latest API and improves Windows platform support for CI/CD workflows.

## Features

### Enhanced Windows Platform Support

The integration test workflow now includes comprehensive Windows support with PowerShell-specific configurations. This includes separate registry configuration steps for Unix and Windows platforms, ensuring proper package authentication across all environments. The workflow now uses the `ai-run-windows-gpu` runner for better GPU-accelerated testing on Windows.

### Prebuild Package Renaming Support

Added automatic renaming of prebuild packages from `tetherto__*` to `qvac__*` format during the package download process. This ensures consistency across all platforms and simplifies the build artifact management.

## Bug Fixes

### Whisper.cpp API Compatibility

Updated the integration with whisper.cpp to use the new 4-parameter API for `whisper_full()`. The previous 5-parameter version that included an explicit state parameter has been deprecated. The state is now managed internally by the whisper.cpp context, simplifying the code and removing unnecessary state management overhead.

Removed the obsolete `whisper_state` member variable and related initialization code, as the new whisper.cpp API handles state management automatically through the context object.

## Other

Updated the integration test workflow to use a specific version of `bare@1.26.0` for better build consistency. Added the `always-auth=true` configuration for npm registry authentication to improve reliability when downloading private packages.
Loading