Compare commits

..

1 Commits
main ... v1.8.0

Author SHA1 Message Date
SoniaSandler b055802625 chore: 🥁 tagging v1.8.0 🥳 2025-08-05 18:14:47 +00:00
18 changed files with 960 additions and 969 deletions

View File

@ -26,7 +26,7 @@ jobs:
build: build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
name: Install pnpm name: Install pnpm

View File

@ -15,7 +15,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job # Steps represent a sequence of tasks that will be executed as part of the job
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
# Runs a single command using the runners shell # Runs a single command using the runners shell
- name: Compute model size - name: Compute model size
run: ./tools/compute-model-sizes.sh run: ./tools/compute-model-sizes.sh

View File

@ -46,20 +46,20 @@ jobs:
name: Run E2E tests ${{ github.event_name == 'schedule' && '[nightly]' || '' }} name: Run E2E tests ${{ github.event_name == 'schedule' && '[nightly]' || '' }}
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
if: github.event_name == 'workflow_dispatch' if: github.event_name == 'workflow_dispatch'
with: with:
repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }}
ref: ${{ github.event.inputs.branch }} ref: ${{ github.event.inputs.branch }}
path: ${{ github.event.inputs.repositoryName }} path: ${{ github.event.inputs.repositoryName }}
- uses: actions/checkout@v5 - uses: actions/checkout@v4
if: github.event_name == 'push' || github.event_name == 'schedule' if: github.event_name == 'push' || github.event_name == 'schedule'
with: with:
path: podman-desktop-extension-ai-lab path: podman-desktop-extension-ai-lab
# Checkout podman desktop # Checkout podman desktop
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
repository: containers/podman-desktop repository: containers/podman-desktop
ref: main ref: main

View File

@ -30,7 +30,7 @@ jobs:
name: publish name: publish
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
with: with:
repository: meta-llama/llama-stack repository: meta-llama/llama-stack
ref: ${{ github.event.inputs.version }} ref: ${{ github.event.inputs.version }}

View File

@ -29,7 +29,7 @@ jobs:
matrix: matrix:
os: [windows-2022, ubuntu-22.04, macos-14] os: [windows-2022, ubuntu-22.04, macos-14]
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
name: Install pnpm name: Install pnpm
@ -74,7 +74,7 @@ jobs:
env: env:
SKIP_INSTALLATION: true SKIP_INSTALLATION: true
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
path: podman-desktop-extension-ai-lab path: podman-desktop-extension-ai-lab
# Set up pnpm # Set up pnpm
@ -88,7 +88,7 @@ jobs:
with: with:
node-version: 22 node-version: 22
# Checkout podman desktop # Checkout podman desktop
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
repository: containers/podman-desktop repository: containers/podman-desktop
ref: main ref: main

View File

@ -36,7 +36,7 @@ jobs:
env: env:
SKIP_INSTALLATION: true SKIP_INSTALLATION: true
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
path: podman-desktop-extension-ai-lab path: podman-desktop-extension-ai-lab
# Set up pnpm # Set up pnpm
@ -50,7 +50,7 @@ jobs:
with: with:
node-version: 22 node-version: 22
# Checkout podman desktop # Checkout podman desktop
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
repository: podman-desktop/podman-desktop repository: podman-desktop/podman-desktop
ref: main ref: main

View File

@ -41,7 +41,7 @@ jobs:
releaseId: ${{ steps.create_release.outputs.id}} releaseId: ${{ steps.create_release.outputs.id}}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ github.event.inputs.branch }} ref: ${{ github.event.inputs.branch }}
- name: Generate tag utilities - name: Generate tag utilities
@ -116,7 +116,7 @@ jobs:
needs: [tag] needs: [tag]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ needs.tag.outputs.githubTag }} ref: ${{ needs.tag.outputs.githubTag }}

View File

@ -32,7 +32,7 @@ jobs:
update-references: update-references:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Get latest ramalama version - name: Get latest ramalama version
id: get_ramalama_version id: get_ramalama_version

View File

@ -3,7 +3,7 @@
"displayName": "ai-lab-monorepo", "displayName": "ai-lab-monorepo",
"description": "ai-lab-monorepo", "description": "ai-lab-monorepo",
"publisher": "redhat", "publisher": "redhat",
"version": "1.9.0-next", "version": "1.8.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": true, "private": true,
"engines": { "engines": {
@ -46,14 +46,14 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.8.1", "@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1", "@commitlint/config-conventional": "^19.8.1",
"@eslint/compat": "^1.3.2", "@eslint/compat": "^1.3.0",
"@typescript-eslint/eslint-plugin": "^8.40.0", "@typescript-eslint/eslint-plugin": "^8.38.0",
"@typescript-eslint/parser": "^8.40.0", "@typescript-eslint/parser": "^8.39.0",
"@vitest/coverage-v8": "^3.2.3", "@vitest/coverage-v8": "^3.2.3",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"commitlint": "^19.8.1", "commitlint": "^19.8.1",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"eslint": "^9.33.0", "eslint": "^9.32.0",
"eslint-import-resolver-custom-alias": "^1.3.2", "eslint-import-resolver-custom-alias": "^1.3.2",
"eslint-import-resolver-typescript": "^4.3.5", "eslint-import-resolver-typescript": "^4.3.5",
"eslint-plugin-etc": "^2.0.3", "eslint-plugin-etc": "^2.0.3",
@ -66,14 +66,14 @@
"eslint-plugin-unicorn": "^60.0.0", "eslint-plugin-unicorn": "^60.0.0",
"globals": "^16.1.0", "globals": "^16.1.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^16.1.5", "lint-staged": "^16.1.4",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"svelte-check": "^4.3.1", "svelte-check": "^4.3.1",
"svelte-eslint-parser": "^1.3.1", "svelte-eslint-parser": "^1.3.1",
"typescript": "5.9.2", "typescript": "5.8.3",
"typescript-eslint": "^8.40.0", "typescript-eslint": "^8.38.0",
"vite": "^7.1.3", "vite": "^7.0.6",
"vitest": "^3.0.5" "vitest": "^3.0.5"
}, },
"workspaces": { "workspaces": {

View File

@ -2,7 +2,7 @@
"name": "ai-lab", "name": "ai-lab",
"displayName": "Podman AI Lab", "displayName": "Podman AI Lab",
"description": "Podman AI Lab lets you work with LLMs locally, exploring AI fundamentals, experimenting with models and prompts, and serving models while maintaining data security and privacy.", "description": "Podman AI Lab lets you work with LLMs locally, exploring AI fundamentals, experimenting with models and prompts, and serving models while maintaining data security and privacy.",
"version": "1.9.0-next", "version": "1.8.0",
"icon": "icon.png", "icon": "icon.png",
"type": "module", "type": "module",
"publisher": "redhat", "publisher": "redhat",
@ -111,15 +111,15 @@
}, },
"dependencies": { "dependencies": {
"@ai-sdk/openai-compatible": "^0.2.16", "@ai-sdk/openai-compatible": "^0.2.16",
"@huggingface/gguf": "^0.2.1", "@huggingface/gguf": "^0.2.0",
"@huggingface/hub": "^2.4.1", "@huggingface/hub": "^2.4.0",
"ai": "^4.3.19", "ai": "^4.3.19",
"express": "^4.21.2", "express": "^4.21.2",
"express-openapi-validator": "^5.5.8", "express-openapi-validator": "^5.5.8",
"isomorphic-git": "^1.33.0", "isomorphic-git": "^1.32.2",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"openai": "^5.15.0", "openai": "^5.11.0",
"postman-code-generators": "^1.14.1", "postman-code-generators": "^1.14.1",
"postman-collection": "^5.1.0", "postman-collection": "^5.1.0",
"semver": "^7.7.2", "semver": "^7.7.2",
@ -140,7 +140,7 @@
"@types/supertest": "^6.0.3", "@types/supertest": "^6.0.3",
"@types/swagger-ui-dist": "^3.30.5", "@types/swagger-ui-dist": "^3.30.5",
"@types/swagger-ui-express": "^4.1.8", "@types/swagger-ui-express": "^4.1.8",
"openapi-typescript": "^7.9.1", "openapi-typescript": "^7.8.0",
"supertest": "^7.1.4", "supertest": "^7.1.4",
"vitest": "^3.0.5" "vitest": "^3.0.5"
} }

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{ {
"whispercpp": { "whispercpp": {
"default": "quay.io/ramalama/ramalama-whisper-server@sha256:010aa34d8734e5e698fb4c5e852e43e5909baa928e3b6e991e1038a1973909ba" "default": "quay.io/ramalama/ramalama-whisper-server@sha256:8986b418c1cd576a214f81dd42c9c7c340debf9b930ac782be08aeacec7feaea"
}, },
"llamacpp": { "llamacpp": {
"default": "quay.io/ramalama/ramalama-llama-server@sha256:4409a5c964382408f3bc08be1314754edaf2dfec1626f31974e34379bfeec41e", "default": "quay.io/ramalama/ramalama-llama-server@sha256:3ca009ed7c1bf97c79f1f8314117dc3f1eced88c8fcfe8b216797bef84b6e440",
"cuda": "quay.io/ramalama/cuda-llama-server@sha256:5e1a3a2508e4b802c8d8c3ecb97ad1778a1b4288fd114562b51fd411bad91841" "cuda": "quay.io/ramalama/cuda-llama-server@sha256:567a284fcf4c210f767e18ae1e5528b0f418889ec4b630996229ab3e49741ee3"
}, },
"openvino": { "openvino": {
"default": "quay.io/ramalama/openvino@sha256:705f3e0a44dcdc2c7b81c3931e42d5ee19d2502bdb5ebddf3f186932a2658e83" "default": "quay.io/ramalama/openvino@sha256:4142ab5d661bdbcbe3b39e2579caa364680dab561a41958bafc9fd95ba324581"
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "frontend-app", "name": "frontend-app",
"displayName": "UI for AI Lab", "displayName": "UI for AI Lab",
"version": "1.9.0-next", "version": "1.8.0",
"type": "module", "type": "module",
"license": "Apache-2.0", "license": "Apache-2.0",
"scripts": { "scripts": {
@ -26,24 +26,24 @@
"devDependencies": { "devDependencies": {
"@sveltejs/vite-plugin-svelte": "5.1.0", "@sveltejs/vite-plugin-svelte": "5.1.0",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.12", "@tailwindcss/vite": "^4.1.7",
"@testing-library/dom": "^10.4.1", "@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.8.0", "@testing-library/jest-dom": "^6.6.4",
"@testing-library/svelte": "^5.2.8", "@testing-library/svelte": "^5.2.8",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@tsconfig/svelte": "^5.0.5", "@tsconfig/svelte": "^5.0.4",
"@types/humanize-duration": "^3.27.4", "@types/humanize-duration": "^3.27.4",
"@typescript-eslint/eslint-plugin": "8.40.0", "@typescript-eslint/eslint-plugin": "8.38.0",
"jsdom": "^26.1.0", "jsdom": "^26.1.0",
"monaco-editor": "^0.52.2", "monaco-editor": "^0.52.2",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"postcss-load-config": "^6.0.1", "postcss-load-config": "^6.0.1",
"svelte": "5.38.2", "svelte": "5.37.2",
"svelte-fa": "^4.0.4", "svelte-fa": "^4.0.4",
"svelte-select": "^5.8.3", "svelte-select": "^5.8.3",
"svelte-markdown": "^0.4.1", "svelte-markdown": "^0.4.1",
"svelte-preprocess": "^6.0.3", "svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.1.12", "tailwindcss": "^4.1.8",
"vitest": "^3.0.5" "vitest": "^3.0.5"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "ai-lab-tests-playwright", "name": "ai-lab-tests-playwright",
"version": "1.9.0-next", "version": "1.8.0",
"description": "Podman Desktop AI Lab extension Playwright E2E tests", "description": "Podman Desktop AI Lab extension Playwright E2E tests",
"scripts": { "scripts": {
"test:e2e": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/", "test:e2e": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/",
@ -10,10 +10,10 @@
"author": "Red Hat", "author": "Red Hat",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.55.0", "@playwright/test": "^1.54.2",
"@podman-desktop/tests-playwright": "1.21.0", "@podman-desktop/tests-playwright": "1.20.2",
"@types/node": "^22", "@types/node": "^22",
"typescript": "^5.9.2", "typescript": "^5.8.3",
"xvfb-maybe": "^0.2.1" "xvfb-maybe": "^0.2.1"
}, },
"type": "module" "type": "module"

View File

@ -227,7 +227,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
// This test is currently failing due to a known issue: https://github.com/containers/podman-desktop-extension-ai-lab/issues/2925 // This test is currently failing due to a known issue: https://github.com/containers/podman-desktop-extension-ai-lab/issues/2925
test.skip(`Download ${model} via API`, async ({ request }) => { test.skip(`Download ${model} via API`, async ({ request }) => {
test.setTimeout(610_000); test.setTimeout(300_000);
const catalogPage = await aiLabPage.navigationBar.openCatalog(); const catalogPage = await aiLabPage.navigationBar.openCatalog();
await catalogPage.waitForLoad(); await catalogPage.waitForLoad();
console.log(`Downloading ${model}...`); console.log(`Downloading ${model}...`);
@ -241,7 +241,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
insecure: false, insecure: false,
stream: true, stream: true,
}, },
timeout: 600_000, timeout: 300_000,
}); });
const body = await response.body(); const body = await response.body();
@ -297,24 +297,24 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
}); });
test(`Download ${modelName} model`, async () => { test(`Download ${modelName} model`, async () => {
test.setTimeout(610_000); test.setTimeout(310_000);
if (!(await catalogPage.isModelDownloaded(modelName))) { if (!(await catalogPage.isModelDownloaded(modelName))) {
await catalogPage.downloadModel(modelName); await catalogPage.downloadModel(modelName);
} }
await playExpect await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions // eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 600_000, intervals: [5_000] }) .poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [5_000] })
.toBeTruthy(); .toBeTruthy();
}); });
test(`Delete ${modelName} model`, async () => { test(`Delete ${modelName} model`, async () => {
test.skip(isWindows, 'Model deletion is currently very buggy in azure cicd'); test.skip(isWindows, 'Model deletion is currently very buggy in azure cicd');
test.setTimeout(610_000); test.setTimeout(310_000);
playExpect(await catalogPage.isModelDownloaded(modelName)).toBeTruthy(); playExpect(await catalogPage.isModelDownloaded(modelName)).toBeTruthy();
await catalogPage.deleteModel(modelName); await catalogPage.deleteModel(modelName);
await playExpect await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions // eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 600_000, intervals: [2_500] }) .poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [2_500] })
.toBeFalsy(); .toBeFalsy();
}); });
}); });
@ -421,17 +421,20 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
.toBe('RUNNING'); .toBe('RUNNING');
}); });
test(`Delete model service and model for ${modelName}`, async () => { test(`Delete model service for ${modelName}`, async () => {
test.setTimeout(150_000); test.setTimeout(150_000);
await cleanupServices(); const modelServicePage = await modelServiceDetailsPage.deleteService();
await deleteAllModels(); await playExpect(modelServicePage.heading).toBeVisible({ timeout: 120_000 });
}); });
}); });
}); });
// Do not use non-instruct models in playground tests. [
// They break out of guilderails and fail the tests. 'ibm-granite/granite-3.3-8b-instruct-GGUF',
['ibm-granite/granite-3.3-8b-instruct-GGUF', 'TheBloke/Mistral-7B-Instruct-v0.2-GGUF'].forEach(modelName => { 'instructlab/granite-7b-lab-GGUF',
'instructlab/merlinite-7b-lab-GGUF',
'TheBloke/Mistral-7B-Instruct-v0.2-GGUF',
].forEach(modelName => {
test.describe.serial(`AI Lab playground creation and deletion for ${modelName}`, { tag: '@smoke' }, () => { test.describe.serial(`AI Lab playground creation and deletion for ${modelName}`, { tag: '@smoke' }, () => {
let catalogPage: AILabCatalogPage; let catalogPage: AILabCatalogPage;
let playgroundsPage: AILabPlaygroundsPage; let playgroundsPage: AILabPlaygroundsPage;
@ -450,7 +453,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
}); });
test(`Download ${modelName} model if not available`, async () => { test(`Download ${modelName} model if not available`, async () => {
test.setTimeout(610_000); test.setTimeout(310_000);
if (!(await catalogPage.isModelDownloaded(modelName))) { if (!(await catalogPage.isModelDownloaded(modelName))) {
await catalogPage.downloadModel(modelName); await catalogPage.downloadModel(modelName);
} }
@ -512,8 +515,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
test.afterAll(`Cleaning up service model`, async () => { test.afterAll(`Cleaning up service model`, async () => {
test.setTimeout(60_000); test.setTimeout(60_000);
await cleanupServices(); await cleanupServiceModels();
await deleteAllModels();
}); });
}); });
}); });
@ -638,15 +640,14 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
await restartApp(appName); await restartApp(appName);
await stopAndDeleteApp(appName); await stopAndDeleteApp(appName);
await cleanupServices(); await cleanupServiceModels();
}); });
test.afterAll(`Ensure cleanup of "${appName}" app, related service, and images`, async ({ navigationBar }) => { test.afterAll(`Ensure cleanup of "${appName}" app, related service, and images`, async ({ navigationBar }) => {
test.setTimeout(150_000); test.setTimeout(150_000);
await stopAndDeleteApp(appName); await stopAndDeleteApp(appName);
await cleanupServices(); await cleanupServiceModels();
await deleteAllModels();
await deleteUnusedImages(navigationBar); await deleteUnusedImages(navigationBar);
}); });
}); });
@ -715,7 +716,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
}); });
}); });
async function cleanupServices(): Promise<void> { async function cleanupServiceModels(): Promise<void> {
try { try {
const modelServicePage = await aiLabPage.navigationBar.openServices(); const modelServicePage = await aiLabPage.navigationBar.openServices();
await modelServicePage.waitForLoad(); await modelServicePage.waitForLoad();
@ -726,12 +727,6 @@ async function cleanupServices(): Promise<void> {
} }
} }
async function deleteAllModels(): Promise<void> {
const modelCatalogPage = await aiLabPage.navigationBar.openCatalog();
await modelCatalogPage.waitForLoad();
await modelCatalogPage.deleteAllModels();
}
async function restartApp(appName: string): Promise<void> { async function restartApp(appName: string): Promise<void> {
const aiRunningAppsPage = await aiLabPage.navigationBar.openRunningApps(); const aiRunningAppsPage = await aiLabPage.navigationBar.openRunningApps();
const aiApp = await aiRunningAppsPage.getRowForApp(appName); const aiApp = await aiRunningAppsPage.getRowForApp(appName);

View File

@ -50,12 +50,6 @@ export class AILabCatalogPage extends AILabBasePage {
return undefined; return undefined;
} }
async getModelNameByRow(row: Locator): Promise<string> {
const modelNameCell = row.getByLabel('Model Name');
const modelName = await modelNameCell.textContent();
return modelName?.trim() ?? '';
}
async downloadModel(modelName: string): Promise<void> { async downloadModel(modelName: string): Promise<void> {
const modelRow = await this.getModelRowByName(modelName); const modelRow = await this.getModelRowByName(modelName);
if (!modelRow) { if (!modelRow) {
@ -81,35 +75,16 @@ export class AILabCatalogPage extends AILabBasePage {
} }
async deleteModel(modelName: string): Promise<void> { async deleteModel(modelName: string): Promise<void> {
if (!modelName || modelName.trim() === '') {
console.warn('Model name is empty, skipping deletion.');
return;
}
const modelRow = await this.getModelRowByName(modelName); const modelRow = await this.getModelRowByName(modelName);
if (!modelRow) { if (!modelRow) {
throw new Error(`Model ${modelName} not found`); throw new Error(`Model ${modelName} not found`);
} }
const deleteButton = modelRow.getByRole('button', { name: 'Delete Model' }); const deleteButton = modelRow.getByRole('button', { name: 'Delete Model' });
await playExpect.poll(async () => await deleteButton.isEnabled(), { timeout: 10_000 }).toBeTruthy(); await playExpect(deleteButton).toBeEnabled();
await deleteButton.focus(); await deleteButton.focus();
await deleteButton.click(); await deleteButton.click();
await this.page.waitForTimeout(1_000); await this.page.waitForTimeout(1_000);
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm'); await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
await playExpect.poll(async () => await this.isModelDownloaded(modelName), { timeout: 30_000 }).toBeFalsy();
}
async deleteAllModels(): Promise<void> {
const modelRows = await this.getAllModelRows();
if (modelRows.length === 0) {
return;
}
for (const modelRow of modelRows) {
const modelName = await this.getModelNameByRow(modelRow);
if (await this.isModelDownloaded(modelName)) {
await this.deleteModel(modelName);
}
}
} }
async isModelDownloaded(modelName: string): Promise<boolean> { async isModelDownloaded(modelName: string): Promise<boolean> {

View File

@ -33,7 +33,7 @@ export class AILabStartRecipePage extends AILabBasePage {
super(page, webview, 'Start recipe'); super(page, webview, 'Start recipe');
this.recipeStatus = this.webview.getByRole('status'); this.recipeStatus = this.webview.getByRole('status');
this.applicationDetailsPanel = this.webview.getByLabel('application details panel'); this.applicationDetailsPanel = this.webview.getByLabel('application details panel');
this.startRecipeButton = this.webview.getByRole('button', { name: /^Start .+ recipe$/i }); this.startRecipeButton = this.webview.getByRole('button', { name: /Start(\s+([a-z]+\s+)+)recipe/i });
this.openAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Open AI App' }); this.openAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Open AI App' });
this.deleteAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Delete AI App' }); this.deleteAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Delete AI App' });
} }