Compare commits

...

234 Commits
v1.7.1 ... main

Author SHA1 Message Date
Simon Rey 7b333d6132
feat: add Qwen3 4B to the catalog (#3485)
Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>
2025-08-25 17:46:14 +02:00
dependabot[bot] 260e009ee1
build(deps): bump openai from 5.13.1 to 5.15.0 (#3479)
Bumps [openai](https://github.com/openai/openai-node) from 5.13.1 to 5.15.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.13.1...v5.15.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 20:18:18 +02:00
dependabot[bot] 7078043b31
build(deps-dev): bump @tsconfig/svelte from 5.0.4 to 5.0.5 (#3481)
Bumps [@tsconfig/svelte](https://github.com/tsconfig/bases/tree/HEAD/bases) from 5.0.4 to 5.0.5.
- [Commits](https://github.com/tsconfig/bases/commits/HEAD/bases)

---
updated-dependencies:
- dependency-name: "@tsconfig/svelte"
  dependency-version: 5.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 20:17:45 +02:00
dependabot[bot] c9904b72ba
build(deps-dev): bump @testing-library/jest-dom from 6.7.0 to 6.8.0 (#3482)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.7.0 to 6.8.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.7.0...v6.8.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-version: 6.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-22 20:17:23 +02:00
Simon Rey e758c2c7b5
revert: "fix: migrate JSON catalogs to Typesript (#3475)" (#3477)
This reverts commit fe2803eedc.

Signed-off-by: Simon Rey <51708585+eqqe@users.noreply.github.com>
2025-08-21 17:50:37 +02:00
Simon Rey fe2803eedc
fix: migrate JSON catalogs to Typesript (#3475)
* fix: migrate JSON catalogs to Typesript

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>

* fix: migrate JSON catalogs to Typesript

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>

---------

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>
2025-08-21 14:55:12 +00:00
dependabot[bot] edb858b924
build(deps): bump openai from 5.12.2 to 5.13.1 (#3471)
Bumps [openai](https://github.com/openai/openai-node) from 5.12.2 to 5.13.1.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.12.2...v5.13.1)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.13.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 18:39:32 +00:00
dependabot[bot] bbad58e7e7
build(deps-dev): bump @playwright/test from 1.54.2 to 1.55.0 (#3473)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.54.2 to 1.55.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.54.2...v1.55.0)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.55.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 20:38:43 +02:00
Tibor Dancs 4fe5d75d37
chore(tests): hotfix - fix startRecipeButton locator causing failures for certain apps (#3470)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-08-20 16:05:23 +00:00
dependabot[bot] e67ebb773d
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3463)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.39.0 to 8.40.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.40.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.40.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 07:04:59 +02:00
dependabot[bot] 0847ffd6fe
build(deps-dev): bump @podman-desktop/tests-playwright (#3466)
Bumps [@podman-desktop/tests-playwright](https://github.com/podman-desktop/podman-desktop) from 1.20.2 to 1.21.0.
- [Release notes](https://github.com/podman-desktop/podman-desktop/releases)
- [Changelog](https://github.com/podman-desktop/podman-desktop/blob/main/RELEASE.md)
- [Commits](https://github.com/podman-desktop/podman-desktop/compare/v1.20.2...v1.21.0)

---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.21.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 06:53:48 +02:00
dependabot[bot] baa3f6fa16
build(deps-dev): bump vite from 7.1.2 to 7.1.3 (#3467)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.2 to 7.1.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-20 06:53:15 +02:00
dependabot[bot] 8aec87aec3
build(deps-dev): bump @typescript-eslint/parser from 8.39.1 to 8.40.0 (#3453)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 17:52:19 +02:00
dependabot[bot] ee838d4337
build(deps-dev): bump @tailwindcss/vite from 4.1.11 to 4.1.12 (#3451)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 16:48:31 +02:00
dependabot[bot] 1a65ce7309
build(deps-dev): bump @types/node from 22.17.1 to 22.17.2 (#3452)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 14:23:02 +00:00
Podman Desktop Bot 8ee204265c
chore: update ramalama image references 0.12.0 (#3439)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-08-19 08:56:15 +02:00
dependabot[bot] d12fbe7077
build(deps-dev): bump tailwindcss from 4.1.11 to 4.1.12 (#3445)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) from 4.1.11 to 4.1.12.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.12/packages/tailwindcss)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-version: 4.1.12
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 08:28:27 +02:00
dependabot[bot] f2e50a9d96
build(deps-dev): bump svelte from 5.38.1 to 5.38.2 (#3446)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.38.1 to 5.38.2.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.38.2/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.38.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 08:28:06 +02:00
dependabot[bot] 7c56784f17
build(deps): bump isomorphic-git from 1.32.3 to 1.33.0 (#3441)
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.32.3 to 1.33.0.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Changelog](https://github.com/isomorphic-git/isomorphic-git/blob/main/docs/in-the-news.md)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.32.3...v1.33.0)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 06:51:56 +02:00
dependabot[bot] d7bef61a17
build(deps-dev): bump @testing-library/jest-dom from 6.6.4 to 6.7.0 (#3442)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.6.4 to 6.7.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.6.4...v6.7.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-version: 6.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 06:51:03 +02:00
dependabot[bot] dfb0d129a2
build(deps-dev): bump typescript-eslint from 8.39.1 to 8.40.0 (#3443)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.39.1 to 8.40.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.40.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.40.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-19 06:50:10 +02:00
dependabot[bot] d8b30d9056 build(deps-dev): bump eslint from 9.32.0 to 9.33.0
Bumps [eslint](https://github.com/eslint/eslint) from 9.32.0 to 9.33.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.32.0...v9.33.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.33.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-18 09:41:11 +02:00
dependabot[bot] ec4a50926b build(deps-dev): bump @typescript-eslint/parser from 8.39.0 to 8.39.1
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.39.0 to 8.39.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-18 09:28:06 +02:00
dependabot[bot] 0f51ac9fb8
build(deps-dev): bump vite from 7.1.1 to 7.1.2 (#3435)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.1 to 7.1.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 07:26:09 +00:00
dependabot[bot] 95d855d5dc
build(deps-dev): bump svelte from 5.38.0 to 5.38.1 (#3436)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.38.0 to 5.38.1.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.38.1/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.38.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 09:12:56 +02:00
Tibor Dancs 92cd8d54a6
Fix playground tests, system out of storage errors (#3428)
* chore(tests): fixing model deletion logic

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

* chore(tests): removing playground-incompatible models

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

---------

Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-08-13 10:00:25 +00:00
dependabot[bot] 383bc584bd
build(deps-dev): bump typescript from 5.8.3 to 5.9.2 (#3427)
Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.8.3 to 5.9.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v5.9.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 5.9.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 13:17:57 +00:00
dependabot[bot] b201805cd8
build(deps-dev): bump @types/node from 22.17.0 to 22.17.1 (#3426)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.17.0 to 22.17.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.17.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 13:01:51 +00:00
dependabot[bot] 3ec5fa9d99
build(deps): bump isomorphic-git from 1.32.2 to 1.32.3 (#3425)
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.32.2 to 1.32.3.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Changelog](https://github.com/isomorphic-git/isomorphic-git/blob/main/docs/in-the-news.md)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.32.2...v1.32.3)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.32.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 14:44:26 +02:00
dependabot[bot] 26589bf7c0
build(deps-dev): bump @eslint/compat from 1.3.1 to 1.3.2 (#3424)
Bumps [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v1.3.2/packages/compat)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 1.3.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 11:19:29 +02:00
dependabot[bot] 8f87998e72
build(deps-dev): bump openapi-typescript from 7.8.0 to 7.9.1 (#3417)
Bumps [openapi-typescript](https://github.com/openapi-ts/openapi-typescript/tree/HEAD/packages/openapi-typescript) from 7.8.0 to 7.9.1.
- [Release notes](https://github.com/openapi-ts/openapi-typescript/releases)
- [Changelog](https://github.com/openapi-ts/openapi-typescript/blob/main/packages/openapi-typescript/CHANGELOG.md)
- [Commits](https://github.com/openapi-ts/openapi-typescript/commits/openapi-typescript@7.9.1/packages/openapi-typescript)

---
updated-dependencies:
- dependency-name: openapi-typescript
  dependency-version: 7.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 08:28:29 +00:00
dependabot[bot] 3dff4f9493
build(deps-dev): bump typescript-eslint from 8.39.0 to 8.39.1 (#3418)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.39.0 to 8.39.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.39.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 10:11:59 +02:00
dependabot[bot] 38b8a27a61
build(deps): bump openai from 5.12.0 to 5.12.2 (#3419)
Bumps [openai](https://github.com/openai/openai-node) from 5.12.0 to 5.12.2.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.12.0...v5.12.2)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 10:11:18 +02:00
dependabot[bot] 7758d94952
build(deps): bump actions/checkout from 4 to 5 (#3421)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 10:10:55 +02:00
dependabot[bot] bba111a748
build(deps-dev): bump lint-staged from 16.1.4 to 16.1.5 (#3423)
Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 16.1.4 to 16.1.5.
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v16.1.4...v16.1.5)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-version: 16.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 10:10:26 +02:00
dependabot[bot] be7ca31004
build(deps-dev): bump svelte from 5.37.3 to 5.38.0 (#3411)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.37.3 to 5.38.0.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.38.0/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.38.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 07:58:27 +00:00
dependabot[bot] b7f5074429
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3409)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.38.0 to 8.39.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 09:41:44 +02:00
dependabot[bot] 900c4478f6
build(deps): bump @huggingface/hub from 2.4.0 to 2.4.1 (#3412)
Bumps [@huggingface/hub](https://github.com/huggingface/huggingface.js) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/huggingface/huggingface.js/releases)
- [Commits](https://github.com/huggingface/huggingface.js/commits)

---
updated-dependencies:
- dependency-name: "@huggingface/hub"
  dependency-version: 2.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 09:40:46 +02:00
dependabot[bot] d191cdd38a
build(deps-dev): bump vite from 7.0.6 to 7.1.1 (#3415)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.6 to 7.1.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-11 09:40:08 +02:00
dependabot[bot] 576830d438
build(deps): bump @huggingface/gguf from 0.2.0 to 0.2.1 (#3392)
Bumps [@huggingface/gguf](https://github.com/huggingface/huggingface.js) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/huggingface/huggingface.js/releases)
- [Commits](https://github.com/huggingface/huggingface.js/commits)

---
updated-dependencies:
- dependency-name: "@huggingface/gguf"
  dependency-version: 0.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 09:03:33 +02:00
dependabot[bot] 27d5f6e726
build(deps-dev): bump typescript-eslint from 8.38.0 to 8.39.0 (#3393)
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.38.0 to 8.39.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 09:03:04 +02:00
dependabot[bot] 16fd59fd1d
build(deps): bump openai from 5.11.0 to 5.12.0 (#3397)
Bumps [openai](https://github.com/openai/openai-node) from 5.11.0 to 5.12.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.11.0...v5.12.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 09:02:30 +02:00
dependabot[bot] 8a9df3c634
build(deps-dev): bump svelte from 5.37.2 to 5.37.3 (#3399)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.37.2 to 5.37.3.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.37.3/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.37.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-07 09:02:06 +02:00
Tibor Dancs c640a95eab
chore(ci,test): fix model download timeouts (#3401)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-08-06 11:58:01 +00:00
SoniaSandler 6bab97b462 chore: bump version to 1.9.0
Signed-off-by: SoniaSandler <runner@pkrvmjbmru5nbw0.25nck5mkgfoenofczkdl0zm0ze.cx.internal.cloudapp.net>
2025-08-05 20:23:20 +02:00
Philippe Martin b8e566fd5f
support duplicated tag (#3380)
* fix: support duplicate tags
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix: use llama-stack tag for framework
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-08-05 19:08:39 +02:00
dependabot[bot] 8e6c393958
build(deps-dev): bump lint-staged from 16.1.2 to 16.1.4 (#3387)
Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 16.1.2 to 16.1.4.
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/main/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v16.1.2...v16.1.4)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-version: 16.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 10:03:45 +00:00
dependabot[bot] 6b468253f6
build(deps-dev): bump svelte-eslint-parser from 1.3.0 to 1.3.1 (#3386)
Bumps [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/sveltejs/svelte-eslint-parser/releases)
- [Changelog](https://github.com/sveltejs/svelte-eslint-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte-eslint-parser/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: svelte-eslint-parser
  dependency-version: 1.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 10:03:17 +00:00
dependabot[bot] 8c7e735517
build(deps-dev): bump @typescript-eslint/parser from 8.38.0 to 8.39.0 (#3388)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.38.0 to 8.39.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.39.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 11:46:34 +02:00
dependabot[bot] 34086a3688
build(deps-dev): bump svelte-check from 4.3.0 to 4.3.1 (#3391)
Bumps [svelte-check](https://github.com/sveltejs/language-tools) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check-4.3.0...svelte-check-4.3.1)

---
updated-dependencies:
- dependency-name: svelte-check
  dependency-version: 4.3.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-05 11:44:45 +02:00
dependabot[bot] c05fd38183 build(deps-dev): bump @playwright/test from 1.54.1 to 1.54.2
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.54.1 to 1.54.2.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.54.1...v1.54.2)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.54.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 12:10:53 -07:00
dependabot[bot] 4070e3285f build(deps-dev): bump svelte from 5.37.1 to 5.37.2
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.37.1 to 5.37.2.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.37.2/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.37.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 12:09:14 -07:00
dependabot[bot] fb7b8b16e1 build(deps): bump swagger-ui-dist from 5.27.0 to 5.27.1
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.27.0 to 5.27.1.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.27.0...v5.27.1)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.27.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-01 12:05:51 -07:00
Philippe Martin 3523120f7a
feat: add chat llama-stack (#3377)
* feat: add chat llama-stack
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix: do not have duplicate tags
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* feat: update all recipes to v1.8.0
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-08-01 17:59:23 +02:00
Tibor Dancs 12b59b0664
chore(test): extend playground test scope and allign with Amplitude (#3378)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-08-01 10:32:13 +00:00
dependabot[bot] 5d35a1bc65
build(deps): bump openai from 5.10.2 to 5.11.0 (#3368)
Bumps [openai](https://github.com/openai/openai-node) from 5.10.2 to 5.11.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.10.2...v5.11.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-31 07:18:59 +02:00
dependabot[bot] ed1eabdbf3
build(deps): bump postman-collection from 5.0.2 to 5.1.0 (#3369)
Bumps [postman-collection](https://github.com/postmanlabs/postman-collection) from 5.0.2 to 5.1.0.
- [Release notes](https://github.com/postmanlabs/postman-collection/releases)
- [Changelog](https://github.com/postmanlabs/postman-collection/blob/develop/CHANGELOG.yaml)
- [Commits](https://github.com/postmanlabs/postman-collection/compare/v5.0.2...v5.1.0)

---
updated-dependencies:
- dependency-name: postman-collection
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-30 22:31:42 +02:00
Podman Desktop Bot 417f1d720b
chore: update ramalama image references to 0.11.2 (#3364)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-30 08:27:05 +02:00
dependabot[bot] b0964c71ef
build(deps-dev): bump @types/node from 22.16.5 to 22.17.0 (#3366)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.16.5 to 22.17.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.17.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 23:27:29 +02:00
dependabot[bot] c826b1dcae
build(deps-dev): bump svelte from 5.36.16 to 5.37.1 (#3363)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.36.16 to 5.37.1.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.37.1/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.37.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-29 08:46:16 +00:00
dependabot[bot] 5af2390540
build(deps-dev): bump @testing-library/dom from 10.4.0 to 10.4.1 (#3361)
Bumps [@testing-library/dom](https://github.com/testing-library/dom-testing-library) from 10.4.0 to 10.4.1.
- [Release notes](https://github.com/testing-library/dom-testing-library/releases)
- [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/dom-testing-library/compare/v10.4.0...v10.4.1)

---
updated-dependencies:
- dependency-name: "@testing-library/dom"
  dependency-version: 10.4.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 20:03:46 +00:00
dependabot[bot] e360f59b81
build(deps): bump express-openapi-validator from 5.5.7 to 5.5.8 (#3362)
Bumps [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) from 5.5.7 to 5.5.8.
- [Release notes](https://github.com/cdimascio/express-openapi-validator/releases)
- [Changelog](https://github.com/cdimascio/express-openapi-validator/blob/master/CHANGE_HISTORY.md)
- [Commits](https://github.com/cdimascio/express-openapi-validator/compare/v5.5.7...v5.5.8)

---
updated-dependencies:
- dependency-name: express-openapi-validator
  dependency-version: 5.5.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 19:51:39 +00:00
dependabot[bot] 234dab8661
build(deps-dev): bump svelte from 5.36.16 to 5.37.0 (#3360)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.36.16 to 5.37.0.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.37.0/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.37.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 19:51:15 +00:00
dependabot[bot] 224034c41f
build(deps-dev): bump @testing-library/jest-dom from 6.6.3 to 6.6.4 (#3359)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 6.6.3 to 6.6.4.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.6.3...v6.6.4)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-version: 6.6.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 19:47:07 +00:00
dependabot[bot] 6cfeb54a0c
build(deps-dev): bump eslint from 9.31.0 to 9.32.0 (#3355)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 08:24:43 +02:00
dependabot[bot] 00e6d29423
build(deps): bump isomorphic-git from 1.32.1 to 1.32.2 (#3356)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-28 08:21:35 +02:00
Podman Desktop Bot f7988661dd
chore: update ramalama image references to 0.11.1 (#3342)
* chore: update ramalama image references 0.11.1

* fix: update openvino image reference

Signed-off-by: Jeff MAURY <jmaury@redhat.com>

---------

Signed-off-by: Jeff MAURY <jmaury@redhat.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-25 09:39:13 +02:00
dependabot[bot] ce1dd81e96
build(deps-dev): bump svelte from 5.36.14 to 5.36.16 (#3353)
---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.36.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 20:23:49 +00:00
dependabot[bot] 94d6d79b35
build(deps): bump @huggingface/gguf from 0.1.18 to 0.2.0 (#3352)
---
updated-dependencies:
- dependency-name: "@huggingface/gguf"
  dependency-version: 0.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 22:13:57 +02:00
dependabot[bot] c92c49dff6
build(deps-dev): bump vite from 7.0.5 to 7.0.6 (#3354)
---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 22:13:00 +02:00
Tibor Dancs 29ca0cfb36
chore(ci): switching MAPT to resource-based request (#3351)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-24 12:45:49 +00:00
dependabot[bot] b1bca4a678
build(deps-dev): bump supertest from 7.1.3 to 7.1.4 (#3348)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 08:37:32 +02:00
dependabot[bot] 55d1c38182
build(deps-dev): bump svelte from 5.36.13 to 5.36.14 (#3347)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 08:37:04 +02:00
dependabot[bot] 26b8fa11fc
build(deps): bump @huggingface/gguf from 0.1.17 to 0.1.18 (#3349)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-24 08:36:39 +02:00
dependabot[bot] 67903c3b56
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3345)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.37.0 to 8.38.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.38.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.38.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 21:46:39 +02:00
dependabot[bot] 580504240e
build(deps): bump openai from 5.10.1 to 5.10.2 (#3346)
Bumps [openai](https://github.com/openai/openai-node) from 5.10.1 to 5.10.2.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.10.1...v5.10.2)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.10.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 21:46:13 +02:00
dependabot[bot] 73343765da
build(deps-dev): bump eslint-plugin-unicorn from 59.0.1 to 60.0.0 (#3338)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 06:30:31 +00:00
dependabot[bot] 68ab9d6406
build(deps-dev): bump @typescript-eslint/parser from 8.37.0 to 8.38.0 (#3340)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 06:19:20 +00:00
dependabot[bot] 43d010c8bf
build(deps-dev): bump @types/node from 22.16.4 to 22.16.5 (#3337)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 06:17:47 +00:00
dependabot[bot] f822443165
build(deps-dev): bump typescript-eslint from 8.37.0 to 8.38.0 (#3339)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 08:07:57 +02:00
dependabot[bot] 33b38224d5
build(deps-dev): bump svelte from 5.36.8 to 5.36.13 (#3341)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-22 08:07:32 +02:00
Brian M 17b78dce92
fix: update api/pull to update pd model catalog (#3250)
* fix: update api/pull to update pd model catalog

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: update pullmodel to match downloadmodel in modelmanager

Signed-off-by: Brian <bmahabir@bu.edu>

---------

Signed-off-by: Brian <bmahabir@bu.edu>
2025-07-21 13:24:35 -04:00
Simon Rey 6447b4210b
fix(ci): Update CRIU download URL (#3332)
The `cz.archive.ubuntu.com` mirror for CRIU is currently unavailable, causing CI failures. This commit updates the download URL to use `archive.ubuntu.com` directly, which is a more reliable and generally available mirror.

This change ensures that the Podman update process in CI environments can reliably download and install the `criu` package, preventing build failures related to unreachable repositories.

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>
2025-07-21 11:57:40 +02:00
Tibor Dancs 06accdeb27
chore(ci): fixing default vm size in windows nightly tests (#3331)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-21 09:21:58 +00:00
dependabot[bot] 35f37e0a5c
build(deps-dev): bump svelte from 5.36.7 to 5.36.8 (#3329)
---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.36.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-19 00:21:01 +02:00
dependabot[bot] 255d4e5f04
build(deps-dev): bump @podman-desktop/tests-playwright (#3330)
---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.20.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-19 00:20:30 +02:00
Tibor Dancs 6c3cffbb95
chore(ci): add missing condition, when entire trigger run was skipped, because of failed linter job (#3328)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-18 16:15:32 +00:00
Tibor Dancs 3e625d5145
chore(ci): fix catalog change test status badge in PRs (#3327)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-18 15:44:11 +00:00
Tibor Dancs b066514e78
chore(ci): remove irrelevant runs (#3274)
* chore(ci): remove workflow cancellation, let it go to skipped state

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

* chore(ci): set-up a separate cleanup workflow to trigger upon catalog-change-trigger completion

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

* chore(ci): prepare github context for cleanup job

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

---------

Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-18 10:37:36 +00:00
dependabot[bot] 163105e1a6
build(deps-dev): bump eslint-plugin-svelte from 3.10.1 to 3.11.0 (#3319)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-18 10:19:30 +00:00
dependabot[bot] fb680b7571
build(deps-dev): bump vite from 7.0.4 to 7.0.5 (#3324)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.4 to 7.0.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 07:27:31 +00:00
dependabot[bot] 5023a73f66
build(deps-dev): bump svelte from 5.36.4 to 5.36.7 (#3322)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.36.4 to 5.36.7.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.36.7/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.36.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 21:02:12 +00:00
dependabot[bot] 44eee2f6c4
build(deps): bump openai from 5.10.0 to 5.10.1 (#3323)
Bumps [openai](https://github.com/openai/openai-node) from 5.10.0 to 5.10.1.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.10.0...v5.10.1)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.10.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 22:52:14 +02:00
dependabot[bot] ea15b7f90c
build(deps-dev): bump svelte-check from 4.2.2 to 4.3.0 (#3325)
Bumps [svelte-check](https://github.com/sveltejs/language-tools) from 4.2.2 to 4.3.0.
- [Release notes](https://github.com/sveltejs/language-tools/releases)
- [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check-4.2.2...svelte-check-4.3.0)

---
updated-dependencies:
- dependency-name: svelte-check
  dependency-version: 4.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 22:51:29 +02:00
Tibor Dancs 6164dd85c9
chore(ci): fixup - fix mapt default version in params string (#3321)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-17 10:19:36 +00:00
dependabot[bot] b688fdf802
build(deps-dev): bump svelte from 5.36.1 to 5.36.4 (#3313)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 08:54:31 +02:00
dependabot[bot] a0edc0feba
build(deps): bump swagger-ui-dist from 5.26.2 to 5.27.0 (#3315)
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.26.2 to 5.27.0.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.26.2...v5.27.0)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 22:37:55 +02:00
dependabot[bot] 7bd6a63827
build(deps): bump openai from 5.9.1 to 5.10.0 (#3316)
Bumps [openai](https://github.com/openai/openai-node) from 5.9.1 to 5.10.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.9.1...v5.10.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 22:37:13 +02:00
dependabot[bot] 60fbd3a0fc
build(deps-dev): bump svelte-eslint-parser from 1.2.0 to 1.3.0 (#3317)
Bumps [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/sveltejs/svelte-eslint-parser/releases)
- [Changelog](https://github.com/sveltejs/svelte-eslint-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte-eslint-parser/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: svelte-eslint-parser
  dependency-version: 1.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 22:36:48 +02:00
dependabot[bot] 3c9b382e90
build(deps): bump filesize from 11.0.1 to 11.0.2 (#3318)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-16 17:47:25 +00:00
dependabot[bot] d5829cdf80
build(deps-dev): bump svelte from 5.35.6 to 5.36.1 (#3305)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-16 07:01:36 +00:00
dependabot[bot] 58076c5ed6
build(deps): bump ai from 4.3.18 to 4.3.19 (#3306)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 19:40:51 +00:00
dependabot[bot] fcc0981995
build(deps-dev): bump @types/node from 22.16.3 to 22.16.4 (#3304)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 21:29:52 +02:00
Podman Desktop Bot 9c5240fb93
chore: update ramalama image references to 0.11.0 (#3300)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-15 21:29:24 +02:00
dependabot[bot] e7dc3b503d
build(deps): bump @ai-sdk/openai-compatible from 0.2.14 to 0.2.16 (#3307)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 21:28:51 +02:00
dependabot[bot] 31daa3137e
build(deps): bump openai from 5.9.0 to 5.9.1 (#3308)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 21:21:50 +02:00
dependabot[bot] cb94c2d37c
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3309)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 21:21:21 +02:00
Tibor Dancs a2a91f50d6
chore(ci): update MAPT version to 0.9.3 (#3276)
* chore(ci): updated windows actions to latest version of MAPT

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>

* chore(ci): adding '--user 0' to fix .pulumi permissions crash

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>

* chore(ci): gh-vars, update parameters passed over to workflow_dispatch and workflow_call

Signed-off-by: Tibor Dancs <tdancs@redhat.com>

---------

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-15 14:08:13 +00:00
Tibor Dancs 7b3131edfc
chore(ci): update default azure instance for windows tests to Standard_D8as_v5 (#3302)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-07-15 12:22:56 +00:00
Simon Rey 5fe17f185e
fix(ci): perform update of ubuntu packages earlier (#3286)
* fix(ci): fix build doing same as podman-desktop repo

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>

* fix(ci): revert ubuntu version to match podman version
https://github.com/containers/podman-desktop-extension-ai-lab/pull/3286#discussion_r2199970365

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>

* fix: better echo message

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>

---------

Signed-off-by: Simon Rey <sfbrey+eqqe@gmail.com>
2025-07-15 09:33:35 +02:00
dependabot[bot] 275118513e
build(deps): bump ai from 4.3.17 to 4.3.18 (#3298)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 08:40:43 +02:00
dependabot[bot] 62728bd9b8
build(deps-dev): bump @typescript-eslint/parser from 8.36.0 to 8.37.0 (#3297)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 08:37:43 +02:00
dependabot[bot] 2608209348 build(deps-dev): bump typescript-eslint from 8.36.0 to 8.37.0
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.36.0 to 8.37.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.37.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.37.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 23:59:12 +02:00
dependabot[bot] 2f54822df8 build(deps-dev): bump eslint from 9.30.1 to 9.31.0
Bumps [eslint](https://github.com/eslint/eslint) from 9.30.1 to 9.31.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.30.1...v9.31.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.31.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 23:48:49 +02:00
dependabot[bot] 957f45d51a build(deps-dev): bump vite from 7.0.3 to 7.0.4
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.3 to 7.0.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 23:48:19 +02:00
dependabot[bot] 1bc1d2dacf build(deps): bump filesize from 11.0.0 to 11.0.1
Bumps [filesize](https://github.com/avoidwork/filesize.js) from 11.0.0 to 11.0.1.
- [Changelog](https://github.com/avoidwork/filesize.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/avoidwork/filesize.js/compare/11.0.0...11.0.1)

---
updated-dependencies:
- dependency-name: filesize
  dependency-version: 11.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 23:47:54 +02:00
Vladimir Lazar 0bf3f8ffc8
chore(test): cleanup machine on finish (#3284)
Signed-off-by: Vladimir Lazar <vlazar@redhat.com>
2025-07-14 11:50:37 +03:00
dependabot[bot] 8b7966f7fb build(deps-dev): bump svelte from 5.35.5 to 5.35.6
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.35.5 to 5.35.6.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.35.6/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.35.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:20:49 +02:00
dependabot[bot] 3600dbd0ad build(deps-dev): bump @playwright/test from 1.53.2 to 1.54.1
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.53.2 to 1.54.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.53.2...v1.54.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.54.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:19:13 +02:00
dependabot[bot] cbc9986a13 build(deps-dev): bump @podman-desktop/tests-playwright
Bumps [@podman-desktop/tests-playwright](https://github.com/podman-desktop/podman-desktop) from 1.20.0 to 1.20.1.
- [Release notes](https://github.com/podman-desktop/podman-desktop/releases)
- [Changelog](https://github.com/podman-desktop/podman-desktop/blob/main/RELEASE.md)
- [Commits](https://github.com/podman-desktop/podman-desktop/compare/v1.20.0...v1.20.1)

---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.20.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:07:51 +02:00
dependabot[bot] 62fcfee7db build(deps): bump filesize from 10.1.6 to 11.0.0
Bumps [filesize](https://github.com/avoidwork/filesize.js) from 10.1.6 to 11.0.0.
- [Changelog](https://github.com/avoidwork/filesize.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/avoidwork/filesize.js/compare/10.1.6...11.0.0)

---
updated-dependencies:
- dependency-name: filesize
  dependency-version: 11.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:07:36 +02:00
dependabot[bot] 75baaed2a0 build(deps): bump openai from 5.8.3 to 5.9.0
Bumps [openai](https://github.com/openai/openai-node) from 5.8.3 to 5.9.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.8.3...v5.9.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:07:01 +02:00
dependabot[bot] 7d43bce9f2 build(deps-dev): bump @types/node from 22.16.2 to 22.16.3
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.16.2 to 22.16.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.16.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-13 21:06:46 +02:00
Vladimir Lazar 39468474c4
chore(test): better handling for race conditions (#3275)
* chore(test): better handling for race conditions
2025-07-10 15:32:02 +03:00
Podman Desktop Bot b36b5d40c8
chore: update ramalama image references to 0.10.1 (#3267)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeff MAURY <jmaury@redhat.com>
2025-07-10 14:17:50 +02:00
dependabot[bot] 25d8ee1903 build(deps-dev): bump @types/node from 22.16.1 to 22.16.2
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.16.1 to 22.16.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.16.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-09 23:21:41 +02:00
dependabot[bot] 1f6d62d6a3 build(deps-dev): bump svelte from 5.35.4 to 5.35.5
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.35.4 to 5.35.5.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.35.5/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.35.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-09 23:10:23 +02:00
Brian M b8583e962f
feat: added ability to select runtime for playground (#3171)
* feat: added ability to select runtime for playground

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: updated runtime selection filter

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: adjusted tests with new filter change

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: removed none and enforced runtime exlusion

Signed-off-by: Brian <bmahabir@bu.edu>

* chore: edited e2e test to comply with new runtime selection

Signed-off-by: Brian <bmahabir@bu.edu>

* revert: model preset dependent on runtime it should always be preset

Signed-off-by: Brian <bmahabir@bu.edu>

* feat: get the inference provider from a registered store

Signed-off-by: Brian <bmahabir@bu.edu>

* chore: reworked getting registered providers

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: working on refractoring change to use recommended runtime from

config store

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: use config store to preselect runtime fixed tests

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: added back openvino selection

this will show on mac as well

Signed-off-by: Brian <bmahabir@bu.edu>

* fix: added back retrieval of registered providers

Signed-off-by: Brian <bmahabir@bu.edu>

---------

Signed-off-by: Brian <bmahabir@bu.edu>
2025-07-08 21:34:34 -04:00
dependabot[bot] c1aadd8ee9
build(deps): bump openai from 5.8.2 to 5.8.3 (#3271)
Bumps [openai](https://github.com/openai/openai-node) from 5.8.2 to 5.8.3.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.8.2...v5.8.3)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-08 21:26:38 +02:00
dependabot[bot] 759cabfcf6 build(deps-dev): bump @types/node from 22.16.0 to 22.16.1
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.16.0 to 22.16.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.16.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 21:02:35 +02:00
dependabot[bot] 436b18838e build(deps-dev): bump @typescript-eslint/eslint-plugin
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.35.1 to 8.36.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.36.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.36.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 20:50:32 +02:00
dependabot[bot] 4aff54d462 build(deps-dev): bump vite from 7.0.2 to 7.0.3
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.2 to 7.0.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 20:49:27 +02:00
dependabot[bot] c7de6c232f build(deps): bump @huggingface/hub from 2.3.0 to 2.4.0
Bumps [@huggingface/hub](https://github.com/huggingface/huggingface.js) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/huggingface/huggingface.js/releases)
- [Commits](https://github.com/huggingface/huggingface.js/commits)

---
updated-dependencies:
- dependency-name: "@huggingface/hub"
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 20:49:12 +02:00
dependabot[bot] 09ca443ac7 build(deps-dev): bump supertest from 7.1.1 to 7.1.3
Bumps [supertest](https://github.com/ladjs/supertest) from 7.1.1 to 7.1.3.
- [Release notes](https://github.com/ladjs/supertest/releases)
- [Commits](https://github.com/ladjs/supertest/compare/v7.1.1...v7.1.3)

---
updated-dependencies:
- dependency-name: supertest
  dependency-version: 7.1.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-08 20:48:55 +02:00
dependabot[bot] d3aea619eb
build(deps): bump ai from 4.3.16 to 4.3.17 (#3264)
Bumps [ai](https://github.com/vercel/ai) from 4.3.16 to 4.3.17.
- [Release notes](https://github.com/vercel/ai/releases)
- [Changelog](https://github.com/vercel/ai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vercel/ai/compare/ai@4.3.16...ai@4.3.17)

---
updated-dependencies:
- dependency-name: ai
  dependency-version: 4.3.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-08 06:33:31 +00:00
dependabot[bot] 7f872a09c8
build(deps): bump swagger-ui-dist from 5.26.0 to 5.26.2 (#3265)
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.26.0 to 5.26.2.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.26.0...v5.26.2)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.26.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-08 06:22:52 +00:00
dependabot[bot] 083c5b46c9 build(deps-dev): bump typescript-eslint from 8.35.1 to 8.36.0
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.35.1 to 8.36.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.36.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.36.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 22:29:09 +02:00
dependabot[bot] 8342b3443d build(deps): bump @huggingface/hub from 2.2.0 to 2.3.0
Bumps [@huggingface/hub](https://github.com/huggingface/huggingface.js) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/huggingface/huggingface.js/releases)
- [Commits](https://github.com/huggingface/huggingface.js/compare/inference-v2.2.0...inference-v2.3.0)

---
updated-dependencies:
- dependency-name: "@huggingface/hub"
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 22:22:58 +02:00
dependabot[bot] 7a52ee3732 build(deps-dev): bump @podman-desktop/tests-playwright
Bumps [@podman-desktop/tests-playwright](https://github.com/podman-desktop/podman-desktop) from 1.19.2 to 1.20.0.
- [Release notes](https://github.com/podman-desktop/podman-desktop/releases)
- [Changelog](https://github.com/podman-desktop/podman-desktop/blob/main/RELEASE.md)
- [Commits](https://github.com/podman-desktop/podman-desktop/compare/v1.19.2...v1.20.0)

---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.20.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 22:17:32 +02:00
Brian M 35938dbaec
fix: exlude none backend from model service creation (#3255)
Signed-off-by: Brian <bmahabir@bu.edu>
2025-07-07 16:16:34 -04:00
dependabot[bot] 856028e86c build(deps-dev): bump @typescript-eslint/parser from 8.35.1 to 8.36.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.35.1 to 8.36.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.36.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.36.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 21:46:30 +02:00
dependabot[bot] d183a3de98 build(deps-dev): bump svelte from 5.35.2 to 5.35.4
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.35.2 to 5.35.4.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.35.4/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.35.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-07 21:38:49 +02:00
dependabot[bot] e7e5b7419b
build(deps-dev): bump svelte from 5.35.0 to 5.35.2 (#3257)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.35.0 to 5.35.2.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.35.2/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.35.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-04 18:26:10 +00:00
dependabot[bot] 422302f39a
build(deps-dev): bump vite from 7.0.0 to 7.0.2 (#3258)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.0 to 7.0.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-04 20:12:39 +02:00
dependabot[bot] 70dc6a1374 build(deps-dev): bump eslint from 9.30.0 to 9.30.1
---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.30.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-04 10:49:05 +02:00
dependabot[bot] 215a94433d build(deps-dev): bump @types/node from 22.15.34 to 22.16.0
---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-04 10:48:57 +02:00
Philippe Martin 9438670d54 chore: fix duplicates in pnpm-lock
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-07-04 10:37:45 +02:00
dependabot[bot] e7bc3b272f
build(deps-dev): bump svelte from 5.34.9 to 5.35.0 (#3253)
---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.35.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-02 20:12:36 +02:00
dependabot[bot] c7f53dbf67
build(deps): bump swagger-ui-dist from 5.25.4 to 5.26.0 (#3254)
---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.26.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-02 20:12:17 +02:00
Jeff MAURY 24e8471b65
chore(build): switch to pnpm 10.12.4 (#3245) 2025-07-02 14:38:33 +02:00
dependabot[bot] a360ab4c19
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3249)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.35.0 to 8.35.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.35.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 20:27:15 +02:00
Jeff MAURY a13ca53150
chore: add llama-stack-playground (#3101)
* chore: add llama-stack-playground

Fixes #3027

Signed-off-by: Jeff MAURY <jmaury@redhat.com>

* fix: remove podman update

Signed-off-by: Jeff MAURY <jmaury@redhat.com>

* fix: use sha256 action references

Signed-off-by: Jeff MAURY <jmaury@redhat.com>

---------

Signed-off-by: Jeff MAURY <jmaury@redhat.com>
2025-07-01 18:57:30 +02:00
Anton Misskii c7de36867e
chore(test): skip Object Detection app install on Windows and Linux CI (#3246)
Signed-off-by: Anton Misskii <amisskii@redhat.com>
2025-07-01 15:51:13 +02:00
dependabot[bot] 3e50e8e85e
build(deps-dev): bump svelte from 5.34.8 to 5.34.9 (#3242)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.8 to 5.34.9.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.9/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 22:06:30 +00:00
dependabot[bot] 40b5b8d3a8
build(deps): bump systeminformation from 5.27.6 to 5.27.7 (#3243)
Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 5.27.6 to 5.27.7.
- [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.27.6...v5.27.7)

---
updated-dependencies:
- dependency-name: systeminformation
  dependency-version: 5.27.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 23:54:05 +02:00
Jeff MAURY 9176457e82
chore: switch to vite 7.0.0 (#3231) 2025-06-30 11:29:55 +02:00
tbabalov 8999037dec
chore(test): InstructLab container test (#3085)
* chore(test): add instructlab container startup test

Signed-off-by: xbabalov <t.babalova.17@gmail.com>
2025-06-30 08:17:50 +02:00
dependabot[bot] 047b1f21f1
build(deps): bump openai from 5.8.0 to 5.8.2 (#3232)
Bumps [openai](https://github.com/openai/openai-node) from 5.8.0 to 5.8.2.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.8.0...v5.8.2)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-27 17:34:39 +00:00
dependabot[bot] 2b71a4916e
build(deps): bump isomorphic-git from 1.31.1 to 1.32.0 (#3230)
Bumps [isomorphic-git](https://github.com/isomorphic-git/isomorphic-git) from 1.31.1 to 1.32.0.
- [Release notes](https://github.com/isomorphic-git/isomorphic-git/releases)
- [Changelog](https://github.com/isomorphic-git/isomorphic-git/blob/main/docs/in-the-news.md)
- [Commits](https://github.com/isomorphic-git/isomorphic-git/compare/v1.31.1...v1.32.0)

---
updated-dependencies:
- dependency-name: isomorphic-git
  dependency-version: 1.32.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-26 19:46:03 +02:00
Anton Misskii 06866ef2ee
chore(test): object detection AI app test (#3158)
* chore(test): object detection app verification

Signed-off-by: Anton Misskii <amisskii@redhat.com>

* chore: description

Signed-off-by: Anton Misskii <amisskii@redhat.com>

---------

Signed-off-by: Anton Misskii <amisskii@redhat.com>
2025-06-26 12:31:16 +02:00
Anton Misskii 04103b7375
chore(test): skip api test (#3223)
Signed-off-by: Anton Misskii <amisskii@redhat.com>
2025-06-26 11:09:21 +02:00
dependabot[bot] 018051441b
build(deps-dev): bump svelte from 5.34.7 to 5.34.8 (#3219)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.7 to 5.34.8.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.8/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-25 18:12:16 +00:00
dependabot[bot] 1b8d37c4a4 build(deps-dev): bump @types/node from 22.15.32 to 22.15.33
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.32 to 22.15.33.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.15.33
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 22:26:17 +02:00
Marcel Bertagnini cbee0effe8 fix: added missing icon brain.woff2 to container build
Signed-off-by: Marcel Bertagnini <mbertagn@redhat.com>
2025-06-24 14:39:40 +02:00
dependabot[bot] 27adf640b2
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3210)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.34.1 to 8.35.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.35.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-24 10:34:26 +00:00
dependabot[bot] 2b612a8698 build(deps): bump openai from 5.5.1 to 5.6.0
Bumps [openai](https://github.com/openai/openai-node) from 5.5.1 to 5.6.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.5.1...v5.6.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-23 20:51:00 +02:00
dependabot[bot] 3cd32e83ce build(deps): bump systeminformation from 5.27.5 to 5.27.6
Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 5.27.5 to 5.27.6.
- [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.27.5...v5.27.6)

---
updated-dependencies:
- dependency-name: systeminformation
  dependency-version: 5.27.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-20 20:38:59 +02:00
dependabot[bot] 098bc8c3ea
build(deps-dev): bump svelte from 5.34.6 to 5.34.7 (#3201)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.6 to 5.34.7.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.7/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-19 22:24:55 +02:00
dependabot[bot] ad157bae31 build(deps-dev): bump @playwright/test from 1.53.0 to 1.53.1
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.53.0 to 1.53.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.53.0...v1.53.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-version: 1.53.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-19 20:13:05 +02:00
dependabot[bot] 033dedbb69 build(deps-dev): bump svelte from 5.34.4 to 5.34.6
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.4 to 5.34.6.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.6/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-18 20:27:32 +02:00
dependabot[bot] 2770d10ad3
build(deps-dev): bump svelte from 5.34.3 to 5.34.4 (#3196)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.3 to 5.34.4.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.4/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 20:57:09 +02:00
dependabot[bot] 66aeb14ca6 build(deps-dev): bump eslint-plugin-sonarjs from 3.0.2 to 3.0.3
Bumps [eslint-plugin-sonarjs](https://github.com/SonarSource/SonarJS) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/SonarSource/SonarJS/releases)
- [Commits](https://github.com/SonarSource/SonarJS/commits)

---
updated-dependencies:
- dependency-name: eslint-plugin-sonarjs
  dependency-version: 3.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-17 19:17:52 +02:00
Philippe Martin c10cd56a73
feat: start a recipe with Llama Stack backend (#3180)
* feat: start a recipe with Llama Stack backend
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix: add a llamaStack dependency to pullRecipe and related
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-16 22:51:27 +02:00
dependabot[bot] d400fabffd
build(deps-dev): bump svelte from 5.34.1 to 5.34.3 (#3188)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.34.1 to 5.34.3.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.3/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 20:00:22 +00:00
dependabot[bot] 36b9530c1f
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3186)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.34.0 to 8.34.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 19:59:44 +00:00
dependabot[bot] 719801c50a
build(deps-dev): bump postcss from 8.5.5 to 8.5.6 (#3181)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.5 to 8.5.6.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.5...8.5.6)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 21:48:48 +02:00
Philippe Martin 300620618c
feat: make model optional for recipes (#3175)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-16 09:38:26 +02:00
dependabot[bot] 3a89f2051f
build(deps-dev): bump svelte from 5.33.19 to 5.34.1 (#3179)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.19 to 5.34.1.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.34.1/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.34.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 20:56:00 +00:00
dependabot[bot] 75a16a2ecb
build(deps-dev): bump @eslint/compat from 1.2.9 to 1.3.0 (#3176)
Bumps [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) from 1.2.9 to 1.3.0.
- [Release notes](https://github.com/eslint/rewrite/releases)
- [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md)
- [Commits](https://github.com/eslint/rewrite/commits/compat-v1.3.0/packages/compat)

---
updated-dependencies:
- dependency-name: "@eslint/compat"
  dependency-version: 1.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 22:44:54 +02:00
Philippe Martin 9211096e6c
refactor: introduce ApplicationOptions (#3174)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-12 15:46:18 +02:00
Philippe Martin 4f9ceed95d
feat: use Llama stack container built from container/podman-ai-lab-stack (#3137)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-12 09:27:32 +02:00
dependabot[bot] fb265b174e
build(deps-dev): bump @podman-desktop/tests-playwright (#3166)
Bumps [@podman-desktop/tests-playwright](https://github.com/podman-desktop/podman-desktop) from 1.19.1 to 1.19.2.
- [Release notes](https://github.com/podman-desktop/podman-desktop/releases)
- [Changelog](https://github.com/podman-desktop/podman-desktop/blob/main/RELEASE.md)
- [Commits](https://github.com/podman-desktop/podman-desktop/compare/v1.19.1...v1.19.2)

---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.19.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 17:57:16 +00:00
dependabot[bot] feafd739c6
build(deps): bump express-openapi-validator from 5.5.6 to 5.5.7 (#3170)
Bumps [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) from 5.5.6 to 5.5.7.
- [Release notes](https://github.com/cdimascio/express-openapi-validator/releases)
- [Changelog](https://github.com/cdimascio/express-openapi-validator/blob/master/CHANGE_HISTORY.md)
- [Commits](https://github.com/cdimascio/express-openapi-validator/compare/v5.5.6...v5.5.7)

---
updated-dependencies:
- dependency-name: express-openapi-validator
  dependency-version: 5.5.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-11 19:41:53 +02:00
Philippe Martin c0ceb3c1ea
fix: make model name column always visible (#3164)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-11 18:05:12 +02:00
dependabot[bot] c3b8a07518
build(deps-dev): bump svelte from 5.33.18 to 5.33.19 (#3163)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.18 to 5.33.19.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.19/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-10 20:09:34 +02:00
dependabot[bot] 107d786be6 build(deps-dev): bump @typescript-eslint/eslint-plugin
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.33.1 to 8.34.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.34.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.34.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 20:30:06 +02:00
dependabot[bot] 385599e1de build(deps-dev): bump svelte from 5.33.14 to 5.33.18
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.14 to 5.33.18.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.18/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 20:29:51 +02:00
dependabot[bot] 24d5022140
build(deps-dev): bump @vitest/coverage-v8 from 3.2.2 to 3.2.3 (#3154)
Bumps [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v3.2.3/packages/coverage-v8)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 3.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 20:18:52 +02:00
Ondrej Dockal 8ea3b2938b
chore(deps): clean up unused dependencies vitest and electron (#3131)
Signed-off-by: Ondrej Dockal <odockal@redhat.com>
2025-06-09 13:52:23 +02:00
dependabot[bot] 051ccd6625
build(deps): bump swagger-ui-dist from 5.22.0 to 5.24.0 (#3144)
Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.22.0 to 5.24.0.
- [Release notes](https://github.com/swagger-api/swagger-ui/releases)
- [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc)
- [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.22.0...v5.24.0)

---
updated-dependencies:
- dependency-name: swagger-ui-dist
  dependency-version: 5.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-06 20:43:25 +02:00
Vladimir Lazar fe89b97e65
chore(test): service cleanup was moved from this afterAll hook and button is no longer available (#3138)
Signed-off-by: Vladimir Lazar <vlazar@redhat.com>
2025-06-06 15:04:41 +03:00
Jeff MAURY 1cc19e4280
chore: ramalama update workflow (#3135) 2025-06-06 12:07:15 +02:00
Philippe Martin 9a3df58bdd
fix: always send mandatory message.content field (#3136)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-06 09:38:59 +02:00
Philippe Martin 4a0408210f
fix: always send mandatory response field (#3134)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-05 16:15:14 +02:00
dependabot[bot] dfe4e93e4f build(deps-dev): bump electron from 36.3.2 to 36.4.0
Bumps [electron](https://github.com/electron/electron) from 36.3.2 to 36.4.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v36.3.2...v36.4.0)

---
updated-dependencies:
- dependency-name: electron
  dependency-version: 36.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-04 20:22:28 +02:00
Jeff MAURY 737a31ec7d
fix: ramalama workflow failing on scheduled run (#3110)
Fixes #3098

Signed-off-by: Jeff MAURY <jmaury@redhat.com>
2025-06-04 18:52:36 +02:00
Anton Misskii 7acf72d0f7
chore(test): Implement custom interaction with Audio To Text AI App service (#3112) 2025-06-04 17:12:54 +02:00
dependabot[bot] d69d649273 build(deps-dev): bump svelte from 5.33.13 to 5.33.14
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.13 to 5.33.14.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.14/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 19:51:25 +02:00
dependabot[bot] 214065cd55 build(deps-dev): bump @sveltejs/vite-plugin-svelte from 5.0.3 to 5.1.0
Bumps [@sveltejs/vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte/tree/HEAD/packages/vite-plugin-svelte) from 5.0.3 to 5.1.0.
- [Release notes](https://github.com/sveltejs/vite-plugin-svelte/releases)
- [Changelog](https://github.com/sveltejs/vite-plugin-svelte/blob/main/packages/vite-plugin-svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/vite-plugin-svelte/commits/@sveltejs/vite-plugin-svelte@5.1.0/packages/vite-plugin-svelte)

---
updated-dependencies:
- dependency-name: "@sveltejs/vite-plugin-svelte"
  dependency-version: 5.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 19:40:08 +02:00
dependabot[bot] cd8ccdd0bf build(deps): bump openai from 5.0.2 to 5.1.0
Bumps [openai](https://github.com/openai/openai-node) from 5.0.2 to 5.1.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v5.0.2...v5.1.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 19:27:00 +02:00
dependabot[bot] 74ef2666ca
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3118)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.33.0 to 8.33.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 22:02:01 +00:00
dependabot[bot] f953f01a8d
build(deps-dev): bump svelte from 5.33.10 to 5.33.13 (#3115)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.10 to 5.33.13.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.13/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.13
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 23:48:55 +02:00
dependabot[bot] 629fedb4b9
build(deps-dev): bump @typescript-eslint/parser from 8.33.0 to 8.33.1 (#3116)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.33.0 to 8.33.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 18:22:57 +00:00
Philippe Martin 4de191b0fe
fix: init models catalog synchronously (#3017)
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-06-02 09:46:12 +02:00
dependabot[bot] e8728576f9
build(deps-dev): bump svelte from 5.33.9 to 5.33.10 (#3109)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.9 to 5.33.10.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.10/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-30 20:10:47 +02:00
dependabot[bot] e51cb90a5b
build(deps): bump openai from 4.103.0 to 5.0.0 (#3106)
Bumps [openai](https://github.com/openai/openai-node) from 4.103.0 to 5.0.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v4.103.0...v5.0.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-30 01:25:42 +00:00
dependabot[bot] 091e148f5c
build(deps-dev): bump svelte from 5.33.4 to 5.33.9 (#3107)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.4 to 5.33.9.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.9/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-29 23:51:58 +02:00
Anton Misskii ff1dc06d3b
chore(fix): increase timeout when restarting an AI app. (#3104)
* chore(fix): increase restart timeout

Signed-off-by: Anton Misskii <amisskii@redhat.com>

* Update ai-lab-extension.spec.ts

Signed-off-by: Anton Misskii <124462506+amisskii@users.noreply.github.com>

---------

Signed-off-by: Anton Misskii <amisskii@redhat.com>
Signed-off-by: Anton Misskii <124462506+amisskii@users.noreply.github.com>
2025-05-29 10:08:19 +02:00
dependabot[bot] eb21be7c99
build(deps-dev): bump tailwindcss from 4.1.7 to 4.1.8 (#3103)
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) from 4.1.7 to 4.1.8.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.8/packages/tailwindcss)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-28 19:31:43 +02:00
Tibor Dancs dfdc3d6e13
chore(test): implementing GPU preferences test (#2887)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-05-28 15:59:34 +02:00
Philippe Martin 1729873d84
feat: read catalog synchronously during init (#3094)
* feat: read catalog synchronously
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* feat: wrap JsonWatcher into Promise
Signed-off-by: Philippe Martin <phmartin@redhat.com>

* test: mock catalogManager
Signed-off-by: Philippe Martin <phmartin@redhat.com>
2025-05-28 13:33:56 +00:00
Tibor Dancs ac72327395
chore(test): update playground test to use most frequent model, increase timeout (#3096)
* chore(test): updating model to ibm-research/granite-3.2-8b-instruct-GGUF

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>

* chore(test): adding playground test to smoke suite, increasing model download timeout

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>

---------

Signed-off-by: Tibor Dancs (work-laptop) <tdancs@redhat.com>
2025-05-28 15:31:17 +02:00
Vladimir Lazar 6210a9016b
chore(test): add restart service model e2e test (#3097)
Signed-off-by: Vladimir Lazar <vlazar@redhat.com>
2025-05-28 15:26:17 +03:00
Anton Misskii 121b41c81f
chore(test): fix playground e2e test (#3087)
Signed-off-by: Anton Misskii <amisskii@redhat.com>
2025-05-28 07:36:18 +02:00
dependabot[bot] 715df73530
build(deps-dev): bump @typescript-eslint/eslint-plugin (#3088)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.32.1 to 8.33.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.33.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.33.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 22:00:51 +02:00
dependabot[bot] 3399bca071
build(deps-dev): bump svelte from 5.33.2 to 5.33.4 (#3092)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.2 to 5.33.4.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.4/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 21:43:59 +02:00
Anton Misskii 4ee9ef4ba5
chore(test): restart and verify app is still running (#3084) 2025-05-27 10:50:13 +02:00
Anton Misskii 4366a091e4
chore(test): function calling e2e test (#3046)
Signed-off-by: Anton Misskii <amisskii@redhat.com>
2025-05-27 09:34:46 +02:00
dependabot[bot] a5c151dc00
build(deps-dev): bump svelte from 5.33.1 to 5.33.2 (#3082)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.1 to 5.33.2.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.2/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-26 23:34:31 +02:00
Tibor Dancs e916fe89b9
chore(test): simplifying and extracting test logic, refactor (#3078)
Signed-off-by: Tibor Dancs <tdancs@redhat.com>
2025-05-26 20:44:59 +02:00
dependabot[bot] 89fe6b34dc build(deps-dev): bump svelte from 5.33.0 to 5.33.1
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.33.0 to 5.33.1.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.1/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-23 20:00:37 +02:00
dependabot[bot] 15038b41d7
build(deps): bump systeminformation from 5.26.1 to 5.26.2 (#3074)
Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 5.26.1 to 5.26.2.
- [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.26.1...v5.26.2)

---
updated-dependencies:
- dependency-name: systeminformation
  dependency-version: 5.26.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-23 17:51:25 +00:00
dependabot[bot] db97acbaaa
build(deps-dev): bump svelte from 5.32.1 to 5.33.0 (#3067)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.32.1 to 5.33.0.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.33.0/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.33.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 18:08:10 +00:00
dependabot[bot] 6d856cffd7
build(deps): bump systeminformation from 5.25.11 to 5.26.1 (#3068)
Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 5.25.11 to 5.26.1.
- [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sebhildebrandt/systeminformation/compare/v5.25.11...v5.26.1)

---
updated-dependencies:
- dependency-name: systeminformation
  dependency-version: 5.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 19:59:01 +02:00
dependabot[bot] bdc90f4ceb build(deps-dev): bump @podman-desktop/tests-playwright
Bumps [@podman-desktop/tests-playwright](https://github.com/podman-desktop/podman-desktop) from 1.18.1 to 1.19.1.
- [Release notes](https://github.com/podman-desktop/podman-desktop/releases)
- [Changelog](https://github.com/podman-desktop/podman-desktop/blob/main/RELEASE.md)
- [Commits](https://github.com/podman-desktop/podman-desktop/compare/v1.18.1...v1.19.1)

---
updated-dependencies:
- dependency-name: "@podman-desktop/tests-playwright"
  dependency-version: 1.19.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:40:11 +02:00
dependabot[bot] 496d480cb8 build(deps-dev): bump svelte from 5.32.0 to 5.32.1
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.32.0 to 5.32.1.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.32.1/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.32.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:40:00 +02:00
dependabot[bot] 7a5c29ec22 build(deps): bump openai from 4.100.0 to 4.101.0
Bumps [openai](https://github.com/openai/openai-node) from 4.100.0 to 4.101.0.
- [Release notes](https://github.com/openai/openai-node/releases)
- [Changelog](https://github.com/openai/openai-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/openai/openai-node/compare/v4.100.0...v4.101.0)

---
updated-dependencies:
- dependency-name: openai
  dependency-version: 4.101.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-21 19:31:15 +02:00
Jeff MAURY a3d4939358
fix: update catalog reference to 1.7.0.2 (#3057) 2025-05-21 16:54:39 +02:00
Vladimir Lazar 20a60f57ec
chore(test): validate for inference server type (#3056)
Signed-off-by: Vladimir Lazar <vlazar@redhat.com>
2025-05-21 15:34:31 +03:00
dependabot[bot] 5152260847
build(deps-dev): bump svelte from 5.31.0 to 5.32.0 (#3054)
Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.31.0 to 5.32.0.
- [Release notes](https://github.com/sveltejs/svelte/releases)
- [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md)
- [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.32.0/packages/svelte)

---
updated-dependencies:
- dependency-name: svelte
  dependency-version: 5.32.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 17:30:05 +00:00
dependabot[bot] f3a9316470 build(deps): bump express-openapi-validator from 5.5.1 to 5.5.2
Bumps [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) from 5.5.1 to 5.5.2.
- [Release notes](https://github.com/cdimascio/express-openapi-validator/releases)
- [Changelog](https://github.com/cdimascio/express-openapi-validator/blob/master/CHANGE_HISTORY.md)
- [Commits](https://github.com/cdimascio/express-openapi-validator/compare/v5.5.1...v5.5.2)

---
updated-dependencies:
- dependency-name: express-openapi-validator
  dependency-version: 5.5.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-20 19:19:08 +02:00
Jeff MAURY a4b853da42
chore: switch to pnpm 10 (#3028) 2025-05-20 11:30:42 +02:00
gastoner 0f85d3f9ce chore: bump version to 1.8.0
Signed-off-by: gastoner <runner@fv-az1692-962.1ofan0iq0ufejkuymfg44m4jgb.ex.internal.cloudapp.net>
2025-05-20 09:13:11 +02:00
Evzen Gasta 48d87e21f9 chore: updated ubuntu runner
Signed-off-by: Evzen Gasta <evzen.ml@seznam.cz>
2025-05-20 08:23:44 +02:00
71 changed files with 3668 additions and 2685 deletions

View File

@ -26,14 +26,9 @@ on:
- 'packages/backend/src/assets/ai.json'
workflow_dispatch:
inputs:
fork:
default: 'containers'
description: 'Podman Desktop repo fork'
type: string
required: true
branch:
default: 'main'
description: 'Podman Desktop repo branch'
podman_desktop_repo_args:
default: 'REPO=podman-desktop,FORK=podman-desktop,BRANCH=main'
description: 'Podman Desktop repo fork and branch'
type: string
required: true
ext_repo_options:
@ -72,24 +67,26 @@ on:
type: string
required: true
azure_vm_size:
default: 'Standard_D8s_v4'
default: ''
description: 'Azure VM size (Standard_E4as_v5 is cheapest, 4core AMD, 32GB RAM)'
type: choice
required: true
required: false
options:
- ''
- Standard_D8as_v5
- Standard_D8s_v4
- Standard_E8as_v5
- Standard_E4as_v5
mapt_params:
default: 'IMAGE=quay.io/redhat-developer/mapt,VERSION_TAG=v0.9.5,CPUS=4,MEMORY=32,EXCLUDED_REGIONS="westindia,centralindia,southindia,australiacentral,australiacentral2,australiaeast,australiasoutheast,southafricanorth,southafricawest"'
description: 'MAPT image, version tag, cpus and memory request, and excluded regions in format IMAGE=xxx,VERSION_TAG=xxx,CPUS=xxx,MEMORY=xxx,EXCLUDED_REGIONS=xxx'
required: true
type: string
jobs:
windows:
name: windows-${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}
runs-on: ubuntu-latest
env:
MAPT_VERSION: v0.7.4
MAPT_IMAGE: quay.io/redhat-developer/mapt
MAPT_EXCLUDED_REGIONS: 'westindia,centralindia,southindia,australiacentral,australiacentral2,australiaeast,australiasoutheast,southafricanorth,southafricawest'
strategy:
fail-fast: false
matrix:
@ -108,10 +105,10 @@ jobs:
version=$(curl https://raw.githubusercontent.com/containers/podman-desktop/main/extensions/podman/packages/extension/src/podman5.json | jq -r '.version')
echo "Default Podman Version from Podman Desktop: ${version}"
echo "PD_PODMAN_VERSION=${version}" >> $GITHUB_ENV
- name: Set the default env. variables
env:
DEFAULT_FORK: 'containers'
DEFAULT_BRANCH: 'main'
DEFAULT_PODMAN_DESKTOP_REPO_ARGS: 'REPO=podman-desktop,FORK=podman-desktop,BRANCH=main'
DEFAULT_NPM_TARGET: 'test:e2e'
DEFAULT_ENV_VARS: 'TEST_PODMAN_MACHINE=true,ELECTRON_ENABLE_INSPECT=true'
DEFAULT_PODMAN_OPTIONS: 'INIT=1,START=1,ROOTFUL=1,NETWORKING=0'
@ -120,44 +117,75 @@ jobs:
DEFAULT_PODMAN_VERSION: "${{ env.PD_PODMAN_VERSION || '5.3.2' }}"
DEFAULT_URL: "https://github.com/containers/podman/releases/download/v$DEFAULT_PODMAN_VERSION/podman-$DEFAULT_PODMAN_VERSION-setup.exe"
DEFAULT_PDE2E_IMAGE_VERSION: 'v0.0.3-windows'
DEFAULT_AZURE_VM_SIZE: 'Standard_D8s_v4'
DEFAULT_MAPT_PARAMS: "IMAGE=${{ vars.MAPT_IMAGE || 'quay.io/redhat-developer/mapt' }},VERSION_TAG=${{ vars.MAPT_VERSION_TAG || 'v0.9.5' }},CPUS=${{ vars.MAPT_CPUS || '4' }},MEMORY=${{ vars.MAPT_MEMORY || '32' }},EXCLUDED_REGIONS=\"${{ vars.MAPT_EXCLUDED_REGIONS || 'westindia,centralindia,southindia,australiacentral,australiacentral2,australiaeast,australiasoutheast,southafricanorth,southafricawest' }}\""
run: |
echo "FORK=${{ github.event.inputs.fork || env.DEFAULT_FORK }}" >> $GITHUB_ENV
echo "BRANCH=${{ github.event.inputs.branch || env.DEFAULT_BRANCH }}" >> $GITHUB_ENV
echo "NPM_TARGET=${{ github.event.inputs.npm_target || env.DEFAULT_NPM_TARGET }}" >> $GITHUB_ENV
echo "ENV_VARS=${{ github.event.inputs.env_vars || env.DEFAULT_ENV_VARS }}" >> $GITHUB_ENV
echo "PODMAN_URL=${{ github.event.inputs.podman_remote_url || env.DEFAULT_URL }}" >> $GITHUB_ENV
echo "PDE2E_IMAGE_VERSION=${{ github.event.inputs.pde2e_image_version || env.DEFAULT_PDE2E_IMAGE_VERSION }}" >> $GITHUB_ENV
echo "${{ github.event.inputs.podman_desktop_repo_args || env.DEFAULT_PODMAN_DESKTOP_REPO_ARGS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "PD_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ github.event.inputs.ext_tests_options || env.DEFAULT_EXT_TESTS_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ github.event.inputs.podman_options || env.DEFAULT_PODMAN_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "PODMAN_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ github.event.inputs.ext_repo_options || env.DEFAULT_EXT_REPO_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "EXT_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "AZURE_VM_SIZE=${{ github.event.inputs.azure_vm_size || env.DEFAULT_AZURE_VM_SIZE }}" >> $GITHUB_ENV
echo "MAPT_VM_SIZE=${{ github.event.inputs.azure_vm_size || '' }}" >> $GITHUB_ENV
echo "${{ github.event.inputs.mapt_params || env.DEFAULT_MAPT_PARAMS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "MAPT_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
- name: Create instance
run: |
# Create instance
podman run -d --name windows-create --rm \
-v ${PWD}:/workspace:z \
-e ARM_TENANT_ID=${{ secrets.ARM_TENANT_ID }} \
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION }} azure \
windows create \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace' \
--conn-details-output '/workspace' \
--windows-version '${{ matrix.windows-version }}' \
--windows-featurepack '${{ matrix.windows-featurepack }}' \
--vmsize '${{ env.AZURE_VM_SIZE }}' \
--tags project=podman-desktop \
--spot-excluded-regions ${{ env.MAPT_EXCLUDED_REGIONS }} \
--spot
# Check logs
podman logs -f windows-create
if [ -z "${{ env.MAPT_VM_SIZE }}" ]; then
echo "MAPT_VM_SIZE is not set, using resources approach"
podman run -d --name windows-create --rm \
-v ${PWD}:/workspace:z \
-e ARM_TENANT_ID=${{ secrets.ARM_TENANT_ID }} \
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
--user 0 \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION_TAG }} azure \
windows create \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace' \
--conn-details-output '/workspace' \
--windows-version '${{ matrix.windows-version }}' \
--windows-featurepack '${{ matrix.windows-featurepack }}' \
--cpus ${{ env.MAPT_CPUS }} \
--memory ${{env.MAPT_MEMORY}} \
--nested-virt \
--tags project=podman-desktop \
--spot-excluded-regions ${{ env.MAPT_EXCLUDED_REGIONS }} \
--spot
# Check logs
podman logs -f windows-create
else
echo "MAPT_VM_SIZE is set to '${{ env.MAPT_VM_SIZE }}', using size approach"
# Create instance with VM size
podman run -d --name windows-create --rm \
-v ${PWD}:/workspace:z \
-e ARM_TENANT_ID=${{ secrets.ARM_TENANT_ID }} \
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
--user 0 \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION_TAG }} azure \
windows create \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace' \
--conn-details-output '/workspace' \
--windows-version '${{ matrix.windows-version }}' \
--windows-featurepack '${{ matrix.windows-featurepack }}' \
--vmsize '${{ env.MAPT_VM_SIZE }}' \
--tags project=podman-desktop \
--spot-excluded-regions ${{ env.MAPT_EXCLUDED_REGIONS }} \
--spot
# Check logs
podman logs -f windows-create
fi
- name: Check instance system info
run: |
@ -221,8 +249,8 @@ jobs:
pd-e2e/builder.ps1 \
-targetFolder pd-e2e \
-resultsFolder results \
-fork ${{ env.FORK }} \
-branch ${{ env.BRANCH }} \
-fork ${{ env.PD_FORK }} \
-branch ${{ env.PD_BRANCH }} \
-envVars ${{ env.ENV_VARS }}
# check logs
podman logs -f pde2e-builder-run
@ -244,8 +272,8 @@ jobs:
-resultsFolder results \
-podmanPath $(cat results/podman-location.log) \
-pdPath "$(cat results/pde2e-binary-path.log | tr '\n' " ")" \
-fork ${{ env.FORK }} \
-branch ${{ env.BRANCH }} \
-fork ${{ env.PD_FORK }} \
-branch ${{ env.PD_BRANCH }} \
-extRepo ${{ env.EXT_REPO }} \
-extFork ${{ env.EXT_FORK }} \
-extBranch ${{ env.EXT_BRANCH }} \
@ -270,7 +298,8 @@ jobs:
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION }} azure \
--user 0 \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION_TAG }} azure \
windows destroy \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace'

View File

@ -26,7 +26,7 @@ jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
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:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
# Runs a single command using the runners shell
- name: Compute model size
run: ./tools/compute-model-sizes.sh

View File

@ -46,20 +46,20 @@ jobs:
name: Run E2E tests ${{ github.event_name == 'schedule' && '[nightly]' || '' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
if: github.event_name == 'workflow_dispatch'
with:
repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }}
ref: ${{ github.event.inputs.branch }}
path: ${{ github.event.inputs.repositoryName }}
- uses: actions/checkout@v4
- uses: actions/checkout@v5
if: github.event_name == 'push' || github.event_name == 'schedule'
with:
path: podman-desktop-extension-ai-lab
# Checkout podman desktop
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: containers/podman-desktop
ref: main
@ -81,15 +81,18 @@ jobs:
- name: Update podman
run: |
# ubuntu version from kubic repository to install podman we need (v5)
ubuntu_version='23.04'
echo "ubuntu version from kubic repository to install podman we need (v5)"
ubuntu_version='23.10'
echo "Add unstable kubic repo into list of available sources and get the repo key"
sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list"
curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add -
# install necessary dependencies for criu package which is not part of 23.04
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
# install criu manually from static location
curl -sLO http://cz.archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "Updating database of packages..."
sudo apt-get update -qq
echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}"
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
echo "install criu manually from static location"
curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "installing/update podman package..."
sudo apt-get -qq -y install podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \
sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \
curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \

View File

@ -0,0 +1,53 @@
#
# Copyright (C) 2025 Red Hat, Inc.
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0
name: llama-stack-playground
on:
workflow_dispatch:
inputs:
version:
description: 'llama-stack tag to use (e.g. main, v0.2.8,...)'
type: string
required: true
jobs:
publish:
name: publish
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
with:
repository: meta-llama/llama-stack
ref: ${{ github.event.inputs.version }}
- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Build manifest and images
run: |
podman manifest create quay.io/podman-ai-lab/llama-stack-playground:${{ github.event.inputs.version }}
podman build --platform linux/amd64,linux/arm64 llama_stack/distribution/ui --manifest quay.io/podman-ai-lab/llama-stack-playground:${{ github.event.inputs.version }}
- name: Login to quay.io
run: podman login quay.io --username ${{ secrets.QUAY_USERNAME }} --password ${{ secrets.QUAY_PASSWORD }}
- name: Push manifest and images to quay.io
run: podman manifest push quay.io/podman-ai-lab/llama-stack-playground:${{ github.event.inputs.version }}

View File

@ -29,7 +29,7 @@ jobs:
matrix:
os: [windows-2022, ubuntu-22.04, macos-14]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: pnpm/action-setup@v4
name: Install pnpm
@ -74,7 +74,7 @@ jobs:
env:
SKIP_INSTALLATION: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: podman-desktop-extension-ai-lab
# Set up pnpm
@ -88,7 +88,7 @@ jobs:
with:
node-version: 22
# Checkout podman desktop
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: containers/podman-desktop
ref: main
@ -96,15 +96,18 @@ jobs:
- name: Update podman
run: |
# ubuntu version from kubic repository to install podman we need (v5)
ubuntu_version='23.04'
echo "ubuntu version from kubic repository to install podman we need (v5)"
ubuntu_version='23.10'
echo "Add unstable kubic repo into list of available sources and get the repo key"
sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list"
curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add -
# install necessary dependencies for criu package which is not part of 23.04
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
# install criu manually from static location
curl -sLO http://cz.archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "Updating database of packages..."
sudo apt-get update -qq
echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}"
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
echo "install criu manually from static location"
curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "installing/update podman package..."
sudo apt-get -qq -y install podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \
sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \
curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \

View File

@ -36,7 +36,7 @@ jobs:
env:
SKIP_INSTALLATION: true
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
path: podman-desktop-extension-ai-lab
# Set up pnpm
@ -50,7 +50,7 @@ jobs:
with:
node-version: 22
# Checkout podman desktop
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
repository: podman-desktop/podman-desktop
ref: main
@ -58,15 +58,18 @@ jobs:
- name: Update podman
run: |
# ubuntu version from kubic repository to install podman we need (v5)
ubuntu_version='23.04'
echo "ubuntu version from kubic repository to install podman we need (v5)"
ubuntu_version='23.10'
echo "Add unstable kubic repo into list of available sources and get the repo key"
sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list"
curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add -
# install necessary dependencies for criu package which is not part of 23.04
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
# install criu manually from static location
curl -sLO http://cz.archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "Updating database of packages..."
sudo apt-get update -qq
echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}"
sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1
echo "install criu manually from static location"
curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb
echo "installing/update podman package..."
sudo apt-get -qq -y install podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \
sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \
curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \
@ -108,7 +111,7 @@ jobs:
- name: Update ramalama image references in AI Lab Extension
working-directory: ./podman-desktop-extension-ai-lab
run: sed -i -E "s/(@sha256:[0-9a-f]+)/:${{ github.event.inputs.tag }}/g" packages/backend/src/assets/inference-images.json
run: sed -i -E "s/(@sha256:[0-9a-f]+)/:${{ github.event_name != 'workflow_dispatch' && 'latest' || github.event.inputs.tag }}/g" packages/backend/src/assets/inference-images.json
- name: Build Image
working-directory: ./podman-desktop-extension-ai-lab

View File

@ -0,0 +1,49 @@
name: recipe-catalog-change-cleanup
on:
workflow_run:
workflows: ["recipe-catalog-change-windows-trigger"]
types:
- completed
jobs:
extract-context:
runs-on: ubuntu-24.04
outputs:
extract-context: ${{ steps.prepare-context.outputs.extract-context }}
trigger-template: ${{ steps.prepare-context.outputs.trigger-template }}
steps:
- name: Prepare context
id: prepare-context
env:
WORKFLOW_RUN: ${{ toJson(github.event.workflow_run) }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Workflow run ID: ${{ fromJson(env.WORKFLOW_RUN).id }}"
echo "Fork owner: ${{ fromJson(env.WORKFLOW_RUN).head_repository.owner.login }}"
echo "Fork repo: ${{ fromJson(env.WORKFLOW_RUN).head_repository.name }}"
echo "Fork branch: ${{ fromJson(env.WORKFLOW_RUN).head_branch }}"
echo "Commit SHA: ${{ fromJson(env.WORKFLOW_RUN).head_sha }}"
echo "Base repo: ${{ fromJson(env.WORKFLOW_RUN).repository.full_name }}"
echo "Conclusion: ${{ fromJson(env.WORKFLOW_RUN).conclusion }}"
# Fetch job conclusions using the GitHub CLI
echo "Fetching jobs for workflow run ID: ${{ fromJson(env.WORKFLOW_RUN).id }}"
gh api \
repos/${{ github.repository }}/actions/runs/${{ fromJson(env.WORKFLOW_RUN).id }}/jobs \
--jq '.jobs[] | "\(.name)=\(.conclusion)"' | while read -r line; do
echo "$line" >> $GITHUB_OUTPUT
done
cat $GITHUB_OUTPUT
cleanup:
runs-on: ubuntu-24.04
needs: extract-context
if: ${{ github.event.workflow_run.conclusion == 'skipped' || (github.event.workflow_run.conclusion == 'success' && needs.extract-context.outputs.trigger-template == 'skipped') }}
steps:
- name: Remove skipped or cancelled workflow run
env:
WORKFLOW_RUN: ${{ toJson(github.event.workflow_run) }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Cleaning up workflow run ID: ${{ fromJson(env.WORKFLOW_RUN).id }}"
gh run delete ${{ fromJson(env.WORKFLOW_RUN).id }} --repo ${{ fromJson(env.WORKFLOW_RUN).repository.full_name }}
echo "Workflow run ID ${{ fromJson(env.WORKFLOW_RUN).id }} has been cleaned up."

View File

@ -45,7 +45,7 @@ on:
pde2e-image-version:
required: false
type: string
azure-vm-size:
mapt_params:
required: false
type: string
@ -53,10 +53,6 @@ jobs:
windows:
name: recipe-catalog-windows-${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}
runs-on: ubuntu-24.04
env:
MAPT_VERSION: v0.7.4
MAPT_IMAGE: quay.io/redhat-developer/mapt
MAPT_EXCLUDED_REGIONS: 'westindia,centralindia,southindia,australiacentral,australiacentral2,australiaeast,australiasoutheast,southafricanorth,southafricawest'
strategy:
fail-fast: false
matrix:
@ -68,14 +64,14 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
status_context="ci/gh/e2e/windows-matrix-${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}"
status_context="catalog-change-windows-matrix-${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}"
echo "status_context=${status_context}" >> "$GITHUB_ENV"
set -xuo
# Status msg
data="{\"state\":\"pending\""
data="${data},\"description\":\"Running recipe tests on catalog change on Windows ${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}\""
data="${data},\"context\":\"$status_context\""
data="${data},\"target_url\":\"https://github.com/${{ inputs.trigger-workflow-base-repo }}/actions/runs/${{ inputs.trigger-workflow-run-id }}\"}"
data="${data},\"target_url\":\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}"
# Create status by API call
curl -L -v -X POST \
-H "Accept: application/vnd.github+json" \
@ -96,12 +92,12 @@ jobs:
DEFAULT_NPM_TARGET: 'test:e2e'
DEFAULT_ENV_VARS: 'TEST_PODMAN_MACHINE=true,ELECTRON_ENABLE_INSPECT=true'
DEFAULT_PODMAN_OPTIONS: 'INIT=1,START=1,ROOTFUL=1,NETWORKING=0'
DEFAULT_EXT_TESTS_OPTIONS: 'EXT_RUN_TESTS_FROM_EXTENSION=1,EXT_RUN_TESTS_AS_ADMIN=1'
DEFAULT_EXT_TESTS_OPTIONS: 'EXT_RUN_TESTS_FROM_EXTENSION=1,EXT_RUN_TESTS_AS_ADMIN=1,EXT_TEST_GPU_SUPPORT_ENABLED=0'
DEFAULT_EXT_REPO_OPTIONS: 'REPO=podman-desktop-extension-ai-lab,FORK=containers,BRANCH=main'
DEFAULT_PODMAN_VERSION: "${{ env.PD_PODMAN_VERSION || '5.3.2' }}"
DEFAULT_URL: "https://github.com/containers/podman/releases/download/v$DEFAULT_PODMAN_VERSION/podman-$DEFAULT_PODMAN_VERSION-setup.exe"
DEFAULT_PDE2E_IMAGE_VERSION: 'v0.0.3-windows'
DEFAULT_AZURE_VM_SIZE: 'Standard_D8as_v5'
DEFAULT_MAPT_PARAMS: "IMAGE=${{ vars.MAPT_IMAGE || 'quay.io/redhat-developer/mapt' }},VERSION_TAG=${{ vars.MAPT_VERSION_TAG || 'v0.9.5' }},CPUS=${{ vars.MAPT_CPUS || '4' }},MEMORY=${{ vars.MAPT_MEMORY || '32' }},EXCLUDED_REGIONS=\"${{ vars.MAPT_EXCLUDED_REGIONS || 'westindia,centralindia,southindia,australiacentral,australiacentral2,australiaeast,australiasoutheast,southafricanorth,southafricawest' }}\""
run: |
echo "FORK=${{ inputs.pd-fork || env.DEFAULT_FORK }}" >> $GITHUB_ENV
echo "BRANCH=${{ inputs.pd-branch || env.DEFAULT_BRANCH }}" >> $GITHUB_ENV
@ -113,12 +109,13 @@ jobs:
echo "DEFAULT_EXT_REPO_OPTIONS=REPO=${{ inputs.trigger-workflow-repo-name }},FORK=${{ inputs.trigger-workflow-fork }},BRANCH=${{ inputs.trigger-workflow-branch }}" >> $GITHUB_ENV
fi
echo "${{ github.event.inputs.ext_tests_options || env.DEFAULT_EXT_TESTS_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print kv[1]"="kv[2]}}' >> $GITHUB_ENV
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ env.DEFAULT_PODMAN_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "PODMAN_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "PODMAN_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ inputs.podman-options || env.DEFAULT_EXT_REPO_OPTIONS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "EXT_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "AZURE_VM_SIZE=${{ inputs.azure-vm-size || env.DEFAULT_AZURE_VM_SIZE }}" >> $GITHUB_ENV
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "EXT_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
echo "${{ github.event.inputs.mapt_params || env.DEFAULT_MAPT_PARAMS }}" | awk -F ',' \
'{for (i=1; i<=NF; i++) {split($i, kv, "="); print "MAPT_"kv[1]"="kv[2]}}' >> $GITHUB_ENV
- name: Create instance
run: |
@ -129,14 +126,17 @@ jobs:
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION }} azure \
--user 0 \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION_TAG }} azure \
windows create \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace' \
--conn-details-output '/workspace' \
--windows-version '${{ matrix.windows-version }}' \
--windows-featurepack '${{ matrix.windows-featurepack }}' \
--vmsize '${{ env.AZURE_VM_SIZE }}' \
--cpus ${{ env.MAPT_CPUS }} \
--memory ${{ env.MAPT_MEMORY }} \
--nested-virt \
--tags project=podman-desktop \
--spot-excluded-regions ${{ env.MAPT_EXCLUDED_REGIONS }} \
--spot
@ -268,8 +268,8 @@ jobs:
data="{\"state\":\"failure\""
fi
data="${data},\"description\":\"Finished recipe tests on catalog change on Windows ${{ matrix.windows-version }}-${{ matrix.windows-featurepack }}\""
data="${data},\"context\":\"$status_context\""
data="${data},\"target_url\":\"https://github.com/${{ inputs.trigger-workflow-base-repo }}/actions/runs/${{ inputs.trigger-workflow-run-id }}\"}"
data="${data},\"context\":\"${{ env.status_context }}\""
data="${data},\"target_url\":\"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}"
# Create status by API call
curl -L -v -X POST \
-H "Accept: application/vnd.github+json" \
@ -287,7 +287,8 @@ jobs:
-e ARM_SUBSCRIPTION_ID=${{ secrets.ARM_SUBSCRIPTION_ID }} \
-e ARM_CLIENT_ID=${{ secrets.ARM_CLIENT_ID }} \
-e ARM_CLIENT_SECRET='${{ secrets.ARM_CLIENT_SECRET }}' \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION }} azure \
--user 0 \
${{ env.MAPT_IMAGE }}:${{ env.MAPT_VERSION_TAG }} azure \
windows destroy \
--project-name 'windows-desktop' \
--backed-url 'file:///workspace'

View File

@ -49,7 +49,6 @@ jobs:
else
echo "No changes detected in ai.json"
echo "changes-detected=false" >> $GITHUB_OUTPUT
gh run cancel ${{ github.run_id}}
fi
trigger-template:
@ -65,5 +64,5 @@ jobs:
trigger-workflow-branch: ${{ needs.extract-context.outputs.fork-branch }}
trigger-workflow-commit-sha: ${{ needs.extract-context.outputs.commit-sha }}
trigger-workflow-base-repo: ${{ needs.extract-context.outputs.base-repo }}
ext_tests_options: 'EXT_RUN_TESTS_FROM_EXTENSION=1,EXT_RUN_TESTS_AS_ADMIN=0'
ext_tests_options: 'EXT_RUN_TESTS_FROM_EXTENSION=1,EXT_RUN_TESTS_AS_ADMIN=0,EXT_TEST_GPU_SUPPORT_ENABLED=0'
secrets: inherit

View File

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

View File

@ -0,0 +1,54 @@
#!/usr/bin/env bash
#
# Copyright (C) 2025 Red Hat, Inc.
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0
# Script to update ramalama image references in inference-images.json
set -euo pipefail
JSON_PATH="packages/backend/src/assets/inference-images.json"
TMP_JSON="${JSON_PATH}.tmp"
TAG=$1
# Images and their keys in the JSON
IMAGES=(
"whispercpp:ramalama/ramalama-whisper-server:default"
"llamacpp:ramalama/ramalama-llama-server:default"
"llamacpp:ramalama/cuda-llama-server:cuda"
"openvino:ramalama/openvino:default"
)
cp "$JSON_PATH" "$TMP_JSON"
for entry in "${IMAGES[@]}"; do
IFS=":" read -r key image jsonkey <<< "$entry"
digest=$(curl -s "https://quay.io/v2/$image/manifests/$TAG" -H 'Accept: application/vnd.oci.image.index.v1+json' --head | grep -i Docker-Content-Digest | awk -e '{ print $2 }' | tr -d '\r')
# Update the JSON file with the new digest
jq --arg img "quay.io/$image" --arg dig "$digest" --arg key "$key" --arg jsonkey "$jsonkey" \
'(.[$key][$jsonkey]) = ($img + "@" + $dig)' \
"$TMP_JSON" > "$TMP_JSON.new" && mv "$TMP_JSON.new" "$TMP_JSON"
done
# Compare and update if changed
if cmp -s "$JSON_PATH" "$TMP_JSON"; then
echo "No update needed: digests are up to date."
rm "$TMP_JSON"
exit 0
else
mv "$TMP_JSON" "$JSON_PATH"
echo "Updated inference-images.json with latest digests."
exit 10
fi

View File

@ -0,0 +1,87 @@
#
# Copyright (C) 2025 Red Hat, Inc.
#
# 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.
#
# SPDX-License-Identifier: Apache-2.0
# This workflow automatically updates ramalama image digests in inference-images.json
# and creates a pull request with the changes.
name: update-ramalama-references
on:
schedule:
- cron: '0 3 * * *' # Runs daily at 03:00 UTC
workflow_dispatch:
permissions:
contents: write
jobs:
update-references:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Get latest ramalama version
id: get_ramalama_version
run: |
RAMALAMA_VERSION=$(curl -s https://quay.io/v2/ramalama/ramalama-llama-server/tags/list -s | jq .tags[] | grep -E '^"[0-9]+\.[0-9]+\.[0-9]+"$' | sort -V | tail -n 1 | tr -d '"')
echo "RAMALAMA_VERSION=${RAMALAMA_VERSION}" >> $GITHUB_OUTPUT
- name: Check if PR already exists
id: pr_exists
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const branch = `update-ramalama-references-${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}`;
const { data: pulls } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${branch}`,
state: 'open',
});
if (pulls.length > 0) {
core.setOutput('exists', 'true');
} else {
core.setOutput('exists', 'false');
}
- name: Update ramalama image references in inference-images.json
id: update_digests
if: steps.pr_exists.outputs.exists == 'false'
run: |
bash .github/workflows/update-ramalama-references.sh "${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}"
continue-on-error: true
- name: Commit changes
if: steps.pr_exists.outputs.exists == 'false' && steps.update_digests.outcome == 'failure'
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git checkout -b "update-ramalama-references-${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}"
git add packages/backend/src/assets/inference-images.json
git commit -m "chore: update ramalama image references ${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}"
git push origin "update-ramalama-references-${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}"
- name: Create Pull Request
if: steps.pr_exists.outputs.exists == 'false' && steps.update_digests.outcome == 'failure'
run: |
echo -e "update ramalama image references to ${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}" > /tmp/pr-title
pullRequestUrl=$(gh pr create --title "chore: update ramalama image references to ${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}" --body-file /tmp/pr-title --head "update-ramalama-references-${{ steps.get_ramalama_version.outputs.RAMALAMA_VERSION }}" --base "main")
echo "📢 Pull request created: ${pullRequestUrl}"
echo "➡️ Flag the PR as being ready for review"
gh pr ready "${pullRequestUrl}"
env:
GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }}

View File

@ -21,6 +21,7 @@ COPY packages/backend/package.json /extension/
COPY packages/backend/media/ /extension/media
COPY LICENSE /extension/
COPY packages/backend/icon.png /extension/
COPY packages/backend/brain.woff2 /extension/
COPY README.md /extension/
COPY api/openapi.yaml /extension/api/

View File

@ -3,7 +3,7 @@
"displayName": "ai-lab-monorepo",
"description": "ai-lab-monorepo",
"publisher": "redhat",
"version": "1.7.0-next",
"version": "1.9.0-next",
"license": "Apache-2.0",
"private": true,
"engines": {
@ -24,6 +24,7 @@
"test:unit": "pnpm run test:backend && pnpm run test:shared && pnpm run test:frontend",
"test:e2e": "cd tests/playwright && pnpm run test:e2e",
"test:e2e:smoke": "cd tests/playwright && pnpm run test:e2e:smoke",
"test:e2e:instructlab": "cd tests/playwright && pnpm run test:e2e:instructlab",
"typecheck:shared": "tsc --noEmit --project packages/shared",
"typecheck:frontend": "tsc --noEmit --project packages/frontend",
"typecheck:backend": "cd packages/backend && pnpm run typecheck",
@ -45,14 +46,14 @@
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@eslint/compat": "^1.2.9",
"@typescript-eslint/eslint-plugin": "^8.32.1",
"@typescript-eslint/parser": "^8.32.1",
"@vitest/coverage-v8": "^3.0.5",
"@eslint/compat": "^1.3.2",
"@typescript-eslint/eslint-plugin": "^8.40.0",
"@typescript-eslint/parser": "^8.40.0",
"@vitest/coverage-v8": "^3.2.3",
"autoprefixer": "^10.4.21",
"commitlint": "^19.8.1",
"concurrently": "^9.1.2",
"eslint": "^9.27.0",
"eslint": "^9.33.0",
"eslint-import-resolver-custom-alias": "^1.3.2",
"eslint-import-resolver-typescript": "^4.3.5",
"eslint-plugin-etc": "^2.0.3",
@ -60,19 +61,19 @@
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-redundant-undefined": "^1.0.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-sonarjs": "^3.0.2",
"eslint-plugin-svelte": "^3.8.1",
"eslint-plugin-unicorn": "^59.0.1",
"eslint-plugin-sonarjs": "^3.0.3",
"eslint-plugin-svelte": "^3.11.0",
"eslint-plugin-unicorn": "^60.0.0",
"globals": "^16.1.0",
"husky": "^9.1.7",
"lint-staged": "^16.0.0",
"lint-staged": "^16.1.5",
"prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0",
"svelte-check": "^4.2.1",
"svelte-eslint-parser": "^1.2.0",
"typescript": "5.8.3",
"typescript-eslint": "^8.32.1",
"vite": "^6.3.5",
"svelte-check": "^4.3.1",
"svelte-eslint-parser": "^1.3.1",
"typescript": "5.9.2",
"typescript-eslint": "^8.40.0",
"vite": "^7.1.3",
"vitest": "^3.0.5"
},
"workspaces": {
@ -90,7 +91,15 @@
"pnpm": {
"overrides": {
"postman-collection>semver": "^7.5.2"
}
},
"ignoredBuiltDependencies": [
"@scarf/scarf",
"@tailwindcss/oxide",
"esbuild",
"postman-code-generators",
"svelte-preprocess",
"unrs-resolver"
]
},
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
}

View File

@ -2,7 +2,7 @@
"name": "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.",
"version": "1.7.0-next",
"version": "1.9.0-next",
"icon": "icon.png",
"type": "module",
"publisher": "redhat",
@ -110,22 +110,22 @@
"typecheck": "pnpm run generate && tsc --noEmit"
},
"dependencies": {
"@ai-sdk/openai-compatible": "^0.2.14",
"@huggingface/gguf": "^0.1.17",
"@huggingface/hub": "^2.1.0",
"ai": "^4.3.16",
"@ai-sdk/openai-compatible": "^0.2.16",
"@huggingface/gguf": "^0.2.1",
"@huggingface/hub": "^2.4.1",
"ai": "^4.3.19",
"express": "^4.21.2",
"express-openapi-validator": "^5.5.1",
"isomorphic-git": "^1.30.1",
"express-openapi-validator": "^5.5.8",
"isomorphic-git": "^1.33.0",
"js-yaml": "^4.1.0",
"mustache": "^4.2.0",
"openai": "^4.99.0",
"openai": "^5.15.0",
"postman-code-generators": "^1.14.1",
"postman-collection": "^5.0.2",
"postman-collection": "^5.1.0",
"semver": "^7.7.2",
"swagger-ui-dist": "^5.21.0",
"swagger-ui-dist": "^5.27.1",
"swagger-ui-express": "^5.0.1",
"systeminformation": "^5.25.11",
"systeminformation": "^5.27.7",
"xml-js": "^1.6.11"
},
"devDependencies": {
@ -140,8 +140,8 @@
"@types/supertest": "^6.0.3",
"@types/swagger-ui-dist": "^3.30.5",
"@types/swagger-ui-express": "^4.1.8",
"openapi-typescript": "^7.8.0",
"supertest": "^7.1.1",
"openapi-typescript": "^7.9.1",
"supertest": "^7.1.4",
"vitest": "^3.0.5"
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{
"whispercpp": {
"default": "quay.io/ramalama/ramalama-whisper-server@sha256:72bce4bed86e7f72e41c60960dd7b1fd9b5115328f520ddcae5dbdd689376995"
"default": "quay.io/ramalama/ramalama-whisper-server@sha256:010aa34d8734e5e698fb4c5e852e43e5909baa928e3b6e991e1038a1973909ba"
},
"llamacpp": {
"default": "quay.io/ramalama/ramalama-llama-server@sha256:4e56101073e0bd6f2f2e15839b64315656d0dbfc1331a3385f2ae722e13f2279",
"cuda": "quay.io/ramalama/cuda-llama-server@sha256:56efc824e5b3ae6a6a11e9537ed9e2ac05f9f9fc6f2e81a55eb67b662c94fe95"
"default": "quay.io/ramalama/ramalama-llama-server@sha256:4409a5c964382408f3bc08be1314754edaf2dfec1626f31974e34379bfeec41e",
"cuda": "quay.io/ramalama/cuda-llama-server@sha256:5e1a3a2508e4b802c8d8c3ecb97ad1778a1b4288fd114562b51fd411bad91841"
},
"openvino": {
"default": "quay.io/ramalama/openvino@sha256:670d91cc322933cc4263606459317cd4ca3fcfb16d59a46b11dcd498c2cd7cb5"
"default": "quay.io/ramalama/openvino@sha256:705f3e0a44dcdc2c7b81c3931e42d5ee19d2502bdb5ebddf3f186932a2658e83"
}
}

View File

@ -1,3 +1,3 @@
{
"default": "quay.io/podman-ai-lab/distribution-podman-ai-lab@sha256:12a86f62e8623aaeb2a86120a77d274c0e52496d307d2a399969cc1f8f5260c5"
"default": "ghcr.io/containers/podman-ai-lab-stack:8d6a4a9a7c587c0a8e44703dd750355256e7a796"
}

View File

@ -65,6 +65,8 @@ const modelsManager = {
getModelsInfo: vi.fn(),
isModelOnDisk: vi.fn(),
createDownloader: vi.fn(),
getLocalModelsFromDisk: vi.fn(),
sendModelsInfo: vi.fn(),
} as unknown as ModelsManager;
const catalogManager = {
@ -278,6 +280,8 @@ describe.each([undefined, true, false])('/api/pull endpoint, stream is %o', stre
});
test('/api/pull downloads model and returns success', async () => {
const getLocalModelsSpy = vi.spyOn(modelsManager, 'getLocalModelsFromDisk').mockResolvedValue();
const sendModelsInfoSpy = vi.spyOn(modelsManager, 'sendModelsInfo').mockResolvedValue();
expect(server.getListener()).toBeDefined();
vi.mocked(catalogManager.getModelByName).mockReturnValue({
id: 'modelId',
@ -312,6 +316,8 @@ describe.each([undefined, true, false])('/api/pull endpoint, stream is %o', stre
expect(lines[2]).toEqual('{"status":"success"}');
expect(lines[3]).toEqual('');
}
expect(getLocalModelsSpy).toHaveBeenCalledTimes(1);
expect(sendModelsInfoSpy).toHaveBeenCalledTimes(1);
});
test('/api/pull should return an error if an error occurs during download', async () => {

View File

@ -342,7 +342,9 @@ export class ApiServer implements Disposable {
downloader
.perform(modelName)
.then(() => {
.then(async () => {
await this.modelsManager.getLocalModelsFromDisk();
await this.modelsManager.sendModelsInfo();
this.sendResult(
res,
{
@ -505,7 +507,7 @@ export class ApiServer implements Disposable {
res.write(
JSON.stringify({
model: modelName,
response: chunk.choices[0].delta.content,
response: chunk.choices[0].delta.content ?? '',
done: chunk.choices[0].finish_reason === 'stop',
done_reason: chunk.choices[0].finish_reason === 'stop' ? 'stop' : undefined,
}) + '\n',
@ -516,7 +518,7 @@ export class ApiServer implements Disposable {
onNonStreamResponse: response => {
res.status(200).json({
model: modelName,
response: response.choices[0].message.content,
response: response.choices[0].message.content ?? '',
done: true,
done_reason: 'stop',
});
@ -571,7 +573,7 @@ export class ApiServer implements Disposable {
model: modelName,
message: {
role: 'assistant',
content: chunk.choices[0].delta.content,
content: chunk.choices[0].delta.content ?? '',
},
done: chunk.choices[0].finish_reason === 'stop',
done_reason: chunk.choices[0].finish_reason === 'stop' ? 'stop' : undefined,
@ -585,7 +587,7 @@ export class ApiServer implements Disposable {
model: modelName,
message: {
role: 'assistant',
content: response.choices[0].message.content,
content: response.choices[0].message.content ?? '',
},
done: true,
done_reason: 'stop',

View File

@ -31,6 +31,8 @@ import { VMType } from '@shared/models/IPodman';
import { POD_LABEL_MODEL_ID, POD_LABEL_RECIPE_ID } from '../../utils/RecipeConstants';
import type { InferenceServer } from '@shared/models/IInference';
import type { RpcExtension } from '@shared/messages/MessageProxy';
import type { LlamaStackManager } from '../llama-stack/llamaStackManager';
import type { ApplicationOptions } from '../../models/ApplicationOptions';
const taskRegistryMock = {
createTask: vi.fn(),
@ -75,6 +77,10 @@ const recipeManager = {
buildRecipe: vi.fn(),
} as unknown as RecipeManager;
const llamaStackManager = {
getLlamaStackContainer: vi.fn(),
} as unknown as LlamaStackManager;
vi.mock('@podman-desktop/api', () => ({
window: {
withProgress: vi.fn(),
@ -139,6 +145,11 @@ beforeEach(() => {
id: 'fake-task',
}));
vi.mocked(modelsManagerMock.uploadModelToPodmanMachine).mockResolvedValue('downloaded-model-path');
vi.mocked(llamaStackManager.getLlamaStackContainer).mockResolvedValue({
containerId: 'container1',
port: 10001,
playgroundPort: 10002,
});
});
function getInitializedApplicationManager(): ApplicationManager {
@ -151,6 +162,7 @@ function getInitializedApplicationManager(): ApplicationManager {
telemetryMock,
podManager,
recipeManager,
llamaStackManager,
);
manager.init();
@ -160,11 +172,11 @@ function getInitializedApplicationManager(): ApplicationManager {
describe('requestPullApplication', () => {
test('task should be set to error if pull application raise an error', async () => {
vi.mocked(window.withProgress).mockRejectedValue(new Error('pull application error'));
const trackingId = await getInitializedApplicationManager().requestPullApplication(
connectionMock,
recipeMock,
remoteModelMock,
);
const trackingId = await getInitializedApplicationManager().requestPullApplication({
connection: connectionMock,
recipe: recipeMock,
model: remoteModelMock,
});
// ensure the task is created
await vi.waitFor(() => {
@ -290,40 +302,67 @@ describe('startApplication', () => {
});
});
describe('pullApplication', () => {
describe.each([true, false])('pullApplication, with model is %o', withModel => {
let applicationOptions: ApplicationOptions;
beforeEach(() => {
applicationOptions = withModel
? {
connection: connectionMock,
recipe: recipeMock,
model: remoteModelMock,
}
: {
connection: connectionMock,
recipe: recipeMock,
dependencies: {
llamaStack: true,
},
};
});
test('labels should be propagated', async () => {
await getInitializedApplicationManager().pullApplication(connectionMock, recipeMock, remoteModelMock, {
await getInitializedApplicationManager().pullApplication(applicationOptions, {
'test-label': 'test-value',
});
// clone the recipe
expect(recipeManager.cloneRecipe).toHaveBeenCalledWith(recipeMock, {
'test-label': 'test-value',
'model-id': remoteModelMock.id,
});
// download model
expect(modelsManagerMock.requestDownloadModel).toHaveBeenCalledWith(remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
// upload model to podman machine
expect(modelsManagerMock.uploadModelToPodmanMachine).toHaveBeenCalledWith(connectionMock, remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
});
if (withModel) {
// download model
expect(modelsManagerMock.requestDownloadModel).toHaveBeenCalledWith(remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
// upload model to podman machine
expect(modelsManagerMock.uploadModelToPodmanMachine).toHaveBeenCalledWith(connectionMock, remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
}
// build the recipe
expect(recipeManager.buildRecipe).toHaveBeenCalledWith(connectionMock, recipeMock, remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
expect(recipeManager.buildRecipe).toHaveBeenCalledWith(
{
connection: connectionMock,
recipe: recipeMock,
model: withModel ? remoteModelMock : undefined,
dependencies: applicationOptions.dependencies,
},
{
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
},
);
// create AI App task must be created
expect(taskRegistryMock.createTask).toHaveBeenCalledWith('Creating AI App', 'loading', {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
});
// a pod must have been created
@ -332,7 +371,7 @@ describe('pullApplication', () => {
name: expect.any(String),
portmappings: [],
labels: {
[POD_LABEL_MODEL_ID]: remoteModelMock.id,
[POD_LABEL_MODEL_ID]: withModel ? remoteModelMock.id : '<none>',
[POD_LABEL_RECIPE_ID]: recipeMock.id,
},
});
@ -340,7 +379,7 @@ describe('pullApplication', () => {
expect(containerEngine.createContainer).toHaveBeenCalledWith('test-engine-id', {
Image: recipeImageInfoMock.id,
name: expect.any(String),
Env: [],
Env: withModel ? [] : ['MODEL_ENDPOINT=http://host.containers.internal:10001'],
HealthCheck: undefined,
HostConfig: undefined,
Detach: true,
@ -361,34 +400,45 @@ describe('pullApplication', () => {
},
} as InferenceServer,
});
await getInitializedApplicationManager().pullApplication(connectionMock, recipeMock, remoteModelMock, {
vi.mocked(modelsManagerMock.requestDownloadModel).mockResolvedValue('/path/to/model');
await getInitializedApplicationManager().pullApplication(applicationOptions, {
'test-label': 'test-value',
});
// clone the recipe
expect(recipeManager.cloneRecipe).toHaveBeenCalledWith(recipeMock, {
'test-label': 'test-value',
'model-id': remoteModelMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
});
// download model
expect(modelsManagerMock.requestDownloadModel).toHaveBeenCalledWith(remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
// upload model to podman machine
expect(modelsManagerMock.uploadModelToPodmanMachine).not.toHaveBeenCalled();
if (withModel) {
// download model
expect(modelsManagerMock.requestDownloadModel).toHaveBeenCalledWith(remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
// upload model to podman machine
expect(modelsManagerMock.uploadModelToPodmanMachine).not.toHaveBeenCalled();
}
// build the recipe
expect(recipeManager.buildRecipe).toHaveBeenCalledWith(connectionMock, recipeMock, remoteModelMock, {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
});
expect(recipeManager.buildRecipe).toHaveBeenCalledWith(
{
connection: connectionMock,
recipe: recipeMock,
model: withModel ? remoteModelMock : undefined,
dependencies: applicationOptions.dependencies,
},
{
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
},
);
// create AI App task must be created
expect(taskRegistryMock.createTask).toHaveBeenCalledWith('Creating AI App', 'loading', {
'test-label': 'test-value',
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
});
// a pod must have been created
@ -397,7 +447,7 @@ describe('pullApplication', () => {
name: expect.any(String),
portmappings: [],
labels: {
[POD_LABEL_MODEL_ID]: remoteModelMock.id,
[POD_LABEL_MODEL_ID]: withModel ? remoteModelMock.id : '<none>',
[POD_LABEL_RECIPE_ID]: recipeMock.id,
},
});
@ -405,7 +455,9 @@ describe('pullApplication', () => {
expect(containerEngine.createContainer).toHaveBeenCalledWith('test-engine-id', {
Image: recipeImageInfoMock.id,
name: expect.any(String),
Env: ['MODEL_ENDPOINT=http://host.containers.internal:56001'],
Env: withModel
? ['MODEL_ENDPOINT=http://host.containers.internal:56001']
: ['MODEL_ENDPOINT=http://host.containers.internal:10001'],
HealthCheck: undefined,
HostConfig: undefined,
Detach: true,
@ -427,12 +479,12 @@ describe('pullApplication', () => {
},
} as unknown as PodInfo);
await getInitializedApplicationManager().pullApplication(connectionMock, recipeMock, remoteModelMock);
await getInitializedApplicationManager().pullApplication(applicationOptions);
// removing existing application should create a task to notify the user
expect(taskRegistryMock.createTask).toHaveBeenCalledWith('Removing AI App', 'loading', {
'recipe-id': recipeMock.id,
'model-id': remoteModelMock.id,
'model-id': withModel ? remoteModelMock.id : '<none>',
});
// the remove pod should have been called
expect(podManager.removePod).toHaveBeenCalledWith('test-engine-id', 'test-pod-id-existing');
@ -456,22 +508,24 @@ describe('pullApplication', () => {
],
});
await getInitializedApplicationManager().pullApplication(connectionMock, recipeMock, remoteModelMock);
await getInitializedApplicationManager().pullApplication(applicationOptions);
// the remove pod should have been called
expect(containerEngine.createContainer).toHaveBeenCalledWith(
recipeImageInfoMock.engineId,
expect.objectContaining({
HostConfig: {
Mounts: [
{
Mode: 'Z',
Source: 'downloaded-model-path',
Target: '/downloaded-model-path',
Type: 'bind',
},
],
},
HostConfig: withModel
? {
Mounts: [
{
Mode: 'Z',
Source: 'downloaded-model-path',
Target: '/downloaded-model-path',
Type: 'bind',
},
],
}
: undefined,
}),
);
});

View File

@ -16,7 +16,7 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { Recipe, RecipeComponents, RecipeImage } from '@shared/models/IRecipe';
import type { RecipeComponents, RecipeImage } from '@shared/models/IRecipe';
import * as path from 'node:path';
import { containerEngine, Disposable, window, ProgressLocation } from '@podman-desktop/api';
import type {
@ -28,7 +28,6 @@ import type {
PodContainerInfo,
ContainerProviderConnection,
} from '@podman-desktop/api';
import type { ModelInfo } from '@shared/models/IModelInfo';
import type { ModelsManager } from '../modelsManager';
import { getPortsFromLabel, getPortsInfo } from '../../utils/ports';
import { getDurationSecondsSince, timeout } from '../../utils/utils';
@ -55,6 +54,8 @@ import { RECIPE_START_ROUTE } from '../../registries/NavigationRegistry';
import type { RpcExtension } from '@shared/messages/MessageProxy';
import { TaskRunner } from '../TaskRunner';
import { getInferenceType } from '../../utils/inferenceUtils';
import type { LlamaStackManager } from '../llama-stack/llamaStackManager';
import { isApplicationOptionsWithModelInference, type ApplicationOptions } from '../../models/ApplicationOptions';
export class ApplicationManager extends Publisher<ApplicationState[]> implements Disposable {
#applications: ApplicationRegistry<ApplicationState>;
@ -71,6 +72,7 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
private telemetry: TelemetryLogger,
private podManager: PodManager,
private recipeManager: RecipeManager,
private llamaStackManager: LlamaStackManager,
) {
super(rpcExtension, MSG_APPLICATIONS_STATE_UPDATE, () => this.getApplicationsState());
this.#applications = new ApplicationRegistry<ApplicationState>();
@ -78,11 +80,7 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
this.#disposables = [];
}
async requestPullApplication(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
): Promise<string> {
async requestPullApplication(options: ApplicationOptions): Promise<string> {
// create a tracking id to put in the labels
const trackingId: string = getRandomString();
@ -94,23 +92,23 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
.runAsTask(
{
...labels,
'recipe-pulling': recipe.id, // this label should only be on the master task
'recipe-pulling': options.recipe.id, // this label should only be on the master task
},
{
loadingLabel: `Pulling ${recipe.name} recipe`,
errorMsg: err => `Something went wrong while pulling ${recipe.name}: ${String(err)}`,
loadingLabel: `Pulling ${options.recipe.name} recipe`,
errorMsg: err => `Something went wrong while pulling ${options.recipe.name}: ${String(err)}`,
},
() =>
window.withProgress(
{
location: ProgressLocation.TASK_WIDGET,
title: `Pulling ${recipe.name}.`,
title: `Pulling ${options.recipe.name}.`,
details: {
routeId: RECIPE_START_ROUTE,
routeArgs: [recipe.id, trackingId],
routeArgs: [options.recipe.id, trackingId],
},
},
() => this.pullApplication(connection, recipe, model, labels),
() => this.pullApplication(options, labels),
),
)
.catch(() => {});
@ -118,37 +116,43 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
return trackingId;
}
async pullApplication(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
labels: Record<string, string> = {},
): Promise<void> {
async pullApplication(options: ApplicationOptions, labels: Record<string, string> = {}): Promise<void> {
let modelId: string;
if (isApplicationOptionsWithModelInference(options)) {
modelId = options.model.id;
} else {
modelId = '<none>';
}
// clear any existing status / tasks related to the pair recipeId-modelId.
this.taskRegistry.deleteByLabels({
'recipe-id': recipe.id,
'model-id': model.id,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
const startTime = performance.now();
try {
// init application (git clone, models download etc.)
const podInfo: PodInfo = await this.initApplication(connection, recipe, model, labels);
const podInfo: PodInfo = await this.initApplication(options, labels);
// start the pod
await this.runApplication(podInfo, {
...labels,
'recipe-id': recipe.id,
'model-id': model.id,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
// measure init + start time
const durationSeconds = getDurationSecondsSince(startTime);
this.telemetry.logUsage('recipe.pull', { 'recipe.id': recipe.id, 'recipe.name': recipe.name, durationSeconds });
this.telemetry.logUsage('recipe.pull', {
'recipe.id': options.recipe.id,
'recipe.name': options.recipe.name,
durationSeconds,
});
} catch (err: unknown) {
const durationSeconds = getDurationSecondsSince(startTime);
this.telemetry.logError('recipe.pull', {
'recipe.id': recipe.id,
'recipe.name': recipe.name,
'recipe.id': options.recipe.id,
'recipe.name': options.recipe.name,
durationSeconds,
message: 'error pulling application',
error: err,
@ -173,48 +177,54 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
* @param labels
* @private
*/
private async initApplication(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
labels: Record<string, string> = {},
): Promise<PodInfo> {
private async initApplication(options: ApplicationOptions, labels: Record<string, string> = {}): Promise<PodInfo> {
let modelId: string;
if (isApplicationOptionsWithModelInference(options)) {
modelId = options.model.id;
} else {
modelId = '<none>';
}
// clone the recipe
await this.recipeManager.cloneRecipe(recipe, { ...labels, 'model-id': model.id });
await this.recipeManager.cloneRecipe(options.recipe, { ...labels, 'model-id': modelId });
// get model by downloading it or retrieving locally
let modelPath = await this.modelsManager.requestDownloadModel(model, {
...labels,
'recipe-id': recipe.id,
'model-id': model.id,
});
// build all images, one per container (for a basic sample we should have 2 containers = sample app + model service)
const recipeComponents = await this.recipeManager.buildRecipe(connection, recipe, model, {
...labels,
'recipe-id': recipe.id,
'model-id': model.id,
});
// upload model to podman machine if user system is supported
if (!recipeComponents.inferenceServer) {
modelPath = await this.modelsManager.uploadModelToPodmanMachine(connection, model, {
let modelPath: string | undefined;
if (isApplicationOptionsWithModelInference(options)) {
// get model by downloading it or retrieving locally
modelPath = await this.modelsManager.requestDownloadModel(options.model, {
...labels,
'recipe-id': recipe.id,
'model-id': model.id,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
}
// build all images, one per container (for a basic sample we should have 2 containers = sample app + model service)
const recipeComponents = await this.recipeManager.buildRecipe(options, {
...labels,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
if (isApplicationOptionsWithModelInference(options)) {
// upload model to podman machine if user system is supported
if (!recipeComponents.inferenceServer) {
modelPath = await this.modelsManager.uploadModelToPodmanMachine(options.connection, options.model, {
...labels,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
}
}
// first delete any existing pod with matching labels
if (await this.hasApplicationPod(recipe.id, model.id)) {
await this.removeApplication(recipe.id, model.id);
if (await this.hasApplicationPod(options.recipe.id, modelId)) {
await this.removeApplication(options.recipe.id, modelId);
}
// create a pod containing all the containers to run the application
return this.createApplicationPod(connection, recipe, model, recipeComponents, modelPath, {
return this.createApplicationPod(options, recipeComponents, modelPath, {
...labels,
'recipe-id': recipe.id,
'model-id': model.id,
'recipe-id': options.recipe.id,
'model-id': modelId,
});
}
@ -257,11 +267,9 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
}
protected async createApplicationPod(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
options: ApplicationOptions,
components: RecipeComponents,
modelPath: string,
modelPath: string | undefined,
labels?: { [key: string]: string },
): Promise<PodInfo> {
return this.#taskRunner.runAsTask<PodInfo>(
@ -271,25 +279,25 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
errorMsg: err => `Something went wrong while creating pod: ${String(err)}`,
},
async ({ updateLabels }): Promise<PodInfo> => {
const podInfo = await this.createPod(connection, recipe, model, components.images);
const podInfo = await this.createPod(options, components.images);
updateLabels(labels => ({
...labels,
'pod-id': podInfo.Id,
}));
await this.createContainerAndAttachToPod(connection, podInfo, components, model, modelPath);
await this.createContainerAndAttachToPod(options, podInfo, components, modelPath, labels);
return podInfo;
},
);
}
protected async createContainerAndAttachToPod(
connection: ContainerProviderConnection,
options: ApplicationOptions,
podInfo: PodInfo,
components: RecipeComponents,
modelInfo: ModelInfo,
modelPath: string,
modelPath: string | undefined,
labels?: { [key: string]: string },
): Promise<void> {
const vmType = connection.vmType ?? VMType.UNKNOWN;
const vmType = options.connection.vmType ?? VMType.UNKNOWN;
// temporary check to set Z flag or not - to be removed when switching to podman 5
await Promise.all(
components.images.map(async image => {
@ -297,28 +305,39 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
let envs: string[] = [];
let healthcheck: HealthConfig | undefined = undefined;
// if it's a model service we mount the model as a volume
if (image.modelService) {
const modelName = path.basename(modelPath);
hostConfig = {
Mounts: [
{
Target: `/${modelName}`,
Source: modelPath,
Type: 'bind',
Mode: vmType === VMType.QEMU ? undefined : 'Z',
},
],
};
envs = [`MODEL_PATH=/${modelName}`];
envs.push(...getModelPropertiesForEnvironment(modelInfo));
} else if (components.inferenceServer) {
const endPoint = `http://host.containers.internal:${components.inferenceServer.connection.port}`;
envs = [`MODEL_ENDPOINT=${endPoint}`];
} else {
const modelService = components.images.find(image => image.modelService);
if (modelService && modelService.ports.length > 0) {
const endPoint = `http://localhost:${modelService.ports[0]}`;
if (modelPath && isApplicationOptionsWithModelInference(options)) {
if (image.modelService) {
const modelName = path.basename(modelPath);
hostConfig = {
Mounts: [
{
Target: `/${modelName}`,
Source: modelPath,
Type: 'bind',
Mode: vmType === VMType.QEMU ? undefined : 'Z',
},
],
};
envs = [`MODEL_PATH=/${modelName}`];
envs.push(...getModelPropertiesForEnvironment(options.model));
} else if (components.inferenceServer) {
const endPoint = `http://host.containers.internal:${components.inferenceServer.connection.port}`;
envs = [`MODEL_ENDPOINT=${endPoint}`];
} else {
const modelService = components.images.find(image => image.modelService);
if (modelService && modelService.ports.length > 0) {
const endPoint = `http://localhost:${modelService.ports[0]}`;
envs = [`MODEL_ENDPOINT=${endPoint}`];
}
}
} else if (options.dependencies?.llamaStack) {
let stack = await this.llamaStackManager.getLlamaStackContainer();
if (!stack) {
await this.llamaStackManager.createLlamaStackContainer(options.connection, labels ?? {});
stack = await this.llamaStackManager.getLlamaStackContainer();
}
if (stack) {
envs = [`MODEL_ENDPOINT=http://host.containers.internal:${stack.port}`];
}
}
if (image.ports.length > 0) {
@ -346,12 +365,7 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
);
}
protected async createPod(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
images: RecipeImage[],
): Promise<PodInfo> {
protected async createPod(options: ApplicationOptions, images: RecipeImage[]): Promise<PodInfo> {
// find the exposed port of the sample app so we can open its ports on the new pod
const sampleAppImageInfo = images.find(image => !image.modelService);
if (!sampleAppImageInfo) {
@ -378,9 +392,14 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
// create new pod
const labels: Record<string, string> = {
[POD_LABEL_RECIPE_ID]: recipe.id,
[POD_LABEL_MODEL_ID]: model.id,
[POD_LABEL_RECIPE_ID]: options.recipe.id,
};
if (isApplicationOptionsWithModelInference(options)) {
labels[POD_LABEL_MODEL_ID] = options.model.id;
} else {
labels[POD_LABEL_MODEL_ID] = '<none>';
}
// collecting all modelService ports
const modelPorts = images
.filter(img => img.modelService)
@ -398,7 +417,7 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
labels[POD_LABEL_APP_PORTS] = appPorts.join(',');
}
const { engineId, Id } = await this.podManager.createPod({
provider: connection,
provider: options.connection,
name: getRandomName(`pod-${sampleAppImageInfo.appName}`),
portmappings: portmappings,
labels,
@ -635,15 +654,28 @@ export class ApplicationManager extends Publisher<ApplicationState[]> implements
const appPod = await this.getApplicationPod(recipeId, modelId);
await this.removeApplication(recipeId, modelId);
const recipe = this.catalogManager.getRecipeById(recipeId);
const model = this.catalogManager.getModelById(appPod.Labels[POD_LABEL_MODEL_ID]);
let opts: ApplicationOptions;
if (appPod.Labels[POD_LABEL_MODEL_ID] === '<none>') {
opts = {
connection,
recipe,
};
} else {
const model = this.catalogManager.getModelById(appPod.Labels[POD_LABEL_MODEL_ID]);
opts = {
connection,
recipe,
model,
};
}
// init the recipe
const podInfo = await this.initApplication(connection, recipe, model);
const podInfo = await this.initApplication(opts);
// start the pod
return this.runApplication(podInfo, {
'recipe-id': recipe.id,
'model-id': model.id,
'recipe-id': recipeId,
'model-id': modelId,
});
}

View File

@ -96,7 +96,7 @@ beforeEach(async () => {
describe('invalid user catalog', () => {
beforeEach(async () => {
vi.mocked(promises.readFile).mockResolvedValue('invalid json');
catalogManager.init();
await catalogManager.init();
});
test('expect correct model is returned with valid id', () => {
@ -116,7 +116,7 @@ describe('invalid user catalog', () => {
test('expect correct model is returned from default catalog with valid id when no user catalog exists', async () => {
vi.mocked(existsSync).mockReturnValue(false);
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getRecipes().length > 0);
const model = catalogManager.getModelById('llama-2-7b-chat.Q5_K_S');
@ -132,7 +132,7 @@ test('expect correct model is returned with valid id when the user catalog is va
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const model = catalogManager.getModelById('model1');
@ -146,7 +146,7 @@ test('expect to call writeFile in addLocalModelsToCatalog with catalog updated',
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getRecipes().length > 0);
const mtimeDate = new Date('2024-04-03T09:51:15.766Z');
@ -174,7 +174,7 @@ test('expect to call writeFile in removeLocalModelFromCatalog with catalog updat
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
vi.mocked(path.resolve).mockReturnValue('path');
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getRecipes().length > 0);
vi.mocked(promises.writeFile).mockResolvedValue();
@ -196,7 +196,7 @@ test('catalog should be the combination of user catalog and default catalog', as
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
vi.mocked(path.resolve).mockReturnValue('path');
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().length > userContent.models.length);
const mtimeDate = new Date('2024-04-03T09:51:15.766Z');
@ -238,7 +238,7 @@ test('catalog should use user items in favour of default', async () => {
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(overwriteFullCatalog));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().length > 0);
const mtimeDate = new Date('2024-04-03T09:51:15.766Z');
@ -330,7 +330,7 @@ test('filter recipes by language', async () => {
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const result1 = catalogManager.filterRecipes({
languages: ['lang1'],
@ -375,7 +375,7 @@ test('filter recipes by tool', async () => {
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const result1 = catalogManager.filterRecipes({
@ -445,7 +445,7 @@ test('filter recipes by framework', async () => {
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const result1 = catalogManager.filterRecipes({
@ -519,7 +519,7 @@ test('filter recipes by language and framework', async () => {
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const result1 = catalogManager.filterRecipes({
@ -546,7 +546,7 @@ test('filter recipes by language, tool and framework', async () => {
vi.mocked(existsSync).mockReturnValue(true);
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
catalogManager.init();
await catalogManager.init();
await vi.waitUntil(() => catalogManager.getModels().some(model => model.id === 'model1'));
const result1 = catalogManager.filterRecipes({
@ -567,3 +567,15 @@ test('filter recipes by language, tool and framework', async () => {
tools: [{ name: 'tool1', count: 1 }],
});
});
test('models are loaded as soon as init is finished when no user catalog', async () => {
await catalogManager.init();
expect(catalogManager.getModels()).toHaveLength(3);
});
test('models are loaded as soon as init is finished when user catalog exists', async () => {
vi.mocked(promises.readFile).mockResolvedValue(JSON.stringify(userContent));
vi.mocked(existsSync).mockReturnValue(true);
await catalogManager.init();
expect(catalogManager.getModels()).toHaveLength(5);
});

View File

@ -60,16 +60,21 @@ export class CatalogManager extends Publisher<ApplicationCatalog> implements Dis
/**
* The init method will start a watcher on the user catalog.json
*/
init(): void {
// Creating a json watcher
this.#jsonWatcher = new JsonWatcher(this.getUserCatalogPath(), {
version: CatalogFormat.CURRENT,
recipes: [],
models: [],
categories: [],
async init(): Promise<void> {
return new Promise<void>(resolve => {
// Creating a json watcher
this.#jsonWatcher = new JsonWatcher(this.getUserCatalogPath(), {
version: CatalogFormat.CURRENT,
recipes: [],
models: [],
categories: [],
});
this.#jsonWatcher.onContentUpdated(content => {
this.onUserCatalogUpdate(content);
resolve();
});
this.#jsonWatcher.init();
});
this.#jsonWatcher.onContentUpdated(content => this.onUserCatalogUpdate(content));
this.#jsonWatcher.init();
}
private loadDefaultCatalog(): void {

View File

@ -98,6 +98,15 @@ export class InferenceManager extends Publisher<InferenceServer[]> implements Di
return Array.from(this.#servers.values());
}
/**
* Get the Unique registered Inference provider types
*/
public getRegisteredProviders(): InferenceType[] {
const types: InferenceType[] = this.inferenceProviderRegistry.getAll().map(provider => provider.type);
return [...new Set(types)];
}
/**
* return an inference server
* @param containerId the containerId of the inference server

View File

@ -225,7 +225,7 @@ test('getModelsInfo should get models in local directory', async () => {
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
expect(manager.getModelsInfo()).toEqual([
{
@ -277,7 +277,7 @@ test('getModelsInfo should return an empty array if the models folder does not e
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.getLocalModelsFromDisk();
expect(manager.getModelsInfo()).toEqual([]);
if (process.platform === 'win32') {
@ -318,7 +318,7 @@ test('getLocalModelsFromDisk should return undefined Date and size when stat fai
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
expect(manager.getModelsInfo()).toEqual([
{
@ -377,7 +377,7 @@ test('getLocalModelsFromDisk should skip folders containing tmp files', async ()
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
expect(manager.getModelsInfo()).toEqual([
{
@ -417,7 +417,7 @@ test('loadLocalModels should post a message with the message on disk and on cata
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
expect(rpcExtensionMock.fire).toHaveBeenNthCalledWith(2, MSG_NEW_MODELS_STATE, [
{
@ -464,7 +464,7 @@ test('deleteModel deletes the model folder', async () => {
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
await manager.deleteModel('model-id-1');
// check that the model's folder is removed from disk
@ -525,7 +525,7 @@ describe('deleting models', () => {
modelHandlerRegistry,
);
modelHandlerRegistry.register(new URLModelHandler(manager, modelsDir));
manager.init();
await manager.init();
await manager.loadLocalModels();
await manager.deleteModel('model-id-1');
// check that the model's folder is removed from disk
@ -899,7 +899,7 @@ describe('getModelMetadata', () => {
modelHandlerRegistry,
);
manager.init();
await manager.init();
const fakeMetadata: Record<string, string> = {
hello: 'world',
@ -939,7 +939,7 @@ describe('getModelMetadata', () => {
modelHandlerRegistry,
);
manager.init();
await manager.init();
const fakeMetadata: Record<string, string> = {
hello: 'world',
@ -995,7 +995,7 @@ describe('uploadModelToPodmanMachine', () => {
modelHandlerRegistry,
);
manager.init();
await manager.init();
const result = await manager.uploadModelToPodmanMachine(connectionMock, modelMock);
expect(result).toBe('uploader-result');
expect(performMock).toHaveBeenCalledWith(modelMock.id);
@ -1028,7 +1028,7 @@ describe('uploadModelToPodmanMachine', () => {
modelHandlerRegistry,
);
manager.init();
await manager.init();
await manager.uploadModelToPodmanMachine(connectionMock, modelMock);
expect(Uploader).not.toHaveBeenCalled();
});

View File

@ -63,7 +63,7 @@ export class ModelsManager implements Disposable {
this.modelHandlerRegistry.getAll().forEach(handler => handler.onUpdate(this.loadLocalModels));
}
init(): void {
async init(): Promise<void> {
const disposable = this.catalogManager.onUpdate(() => {
this.loadLocalModels().catch((err: unknown) => {
console.error(`Something went wrong when loading local models`, err);
@ -71,9 +71,11 @@ export class ModelsManager implements Disposable {
});
this.#disposables.push(disposable);
this.loadLocalModels().catch((err: unknown) => {
try {
await this.loadLocalModels();
} catch (err: unknown) {
console.error('Something went wrong while trying to load local models', err);
});
}
}
dispose(): void {

View File

@ -30,6 +30,7 @@ import { goarch } from '../../utils/arch';
import { VMType } from '@shared/models/IPodman';
import type { InferenceManager } from '../inference/inferenceManager';
import type { ModelInfo } from '@shared/models/IModelInfo';
import type { ApplicationOptions } from '../../models/ApplicationOptions';
const taskRegistryMock = {
createTask: vi.fn(),
@ -184,21 +185,34 @@ describe('cloneRecipe', () => {
});
});
describe('buildRecipe', () => {
describe.each([true, false])('buildRecipe, with model is %o', withModel => {
let applicationOptions: ApplicationOptions;
beforeEach(() => {
applicationOptions = withModel
? {
connection: connectionMock,
recipe: recipeMock,
model: modelInfoMock,
}
: {
connection: connectionMock,
recipe: recipeMock,
};
});
test('error in build propagate it', async () => {
vi.mocked(builderManagerMock.build).mockRejectedValue(new Error('build error'));
const manager = await getInitializedRecipeManager();
await expect(() => {
return manager.buildRecipe(connectionMock, recipeMock, modelInfoMock);
return manager.buildRecipe(applicationOptions);
}).rejects.toThrowError('build error');
});
test('labels should be propagated', async () => {
const manager = await getInitializedRecipeManager();
await manager.buildRecipe(connectionMock, recipeMock, modelInfoMock, {
await manager.buildRecipe(applicationOptions, {
'test-label': 'test-value',
});

View File

@ -26,12 +26,12 @@ import { parseYamlFile } from '../../models/AIConfig';
import { existsSync, statSync } from 'node:fs';
import { goarch } from '../../utils/arch';
import type { BuilderManager } from './BuilderManager';
import type { ContainerProviderConnection, Disposable } from '@podman-desktop/api';
import type { Disposable } from '@podman-desktop/api';
import { CONFIG_FILENAME } from '../../utils/RecipeConstants';
import type { InferenceManager } from '../inference/inferenceManager';
import type { ModelInfo } from '@shared/models/IModelInfo';
import { withDefaultConfiguration } from '../../utils/inferenceUtils';
import type { InferenceServer } from '@shared/models/IInference';
import { type ApplicationOptions, isApplicationOptionsWithModelInference } from '../../models/ApplicationOptions';
export interface AIContainers {
aiConfigFile: AIConfigFile;
@ -96,73 +96,70 @@ export class RecipeManager implements Disposable {
});
}
public async buildRecipe(
connection: ContainerProviderConnection,
recipe: Recipe,
model: ModelInfo,
labels?: { [key: string]: string },
): Promise<RecipeComponents> {
const localFolder = path.join(this.appUserDirectory, recipe.id);
public async buildRecipe(options: ApplicationOptions, labels?: { [key: string]: string }): Promise<RecipeComponents> {
const localFolder = path.join(this.appUserDirectory, options.recipe.id);
let inferenceServer: InferenceServer | undefined;
// if the recipe has a defined backend, we gives priority to using an inference server
if (recipe.backend && recipe.backend === model.backend) {
let task: Task | undefined;
try {
inferenceServer = this.inferenceManager.findServerByModel(model);
task = this.taskRegistry.createTask('Starting Inference server', 'loading', labels);
if (!inferenceServer) {
const inferenceContainerId = await this.inferenceManager.createInferenceServer(
await withDefaultConfiguration({
modelsInfo: [model],
}),
);
inferenceServer = this.inferenceManager.get(inferenceContainerId);
this.taskRegistry.updateTask({
...task,
labels: {
...task.labels,
containerId: inferenceContainerId,
},
});
} else if (inferenceServer.status === 'stopped') {
await this.inferenceManager.startInferenceServer(inferenceServer.container.containerId);
}
task.state = 'success';
} catch (e) {
// we only skip the task update if the error is that we do not support this backend.
// If so, we build the image for the model service
if (task && String(e) !== 'no enabled provider could be found.') {
task.state = 'error';
task.error = `Something went wrong while starting the inference server: ${String(e)}`;
throw e;
}
} finally {
if (task) {
this.taskRegistry.updateTask(task);
if (isApplicationOptionsWithModelInference(options)) {
// if the recipe has a defined backend, we gives priority to using an inference server
if (options.recipe.backend && options.recipe.backend === options.model.backend) {
let task: Task | undefined;
try {
inferenceServer = this.inferenceManager.findServerByModel(options.model);
task = this.taskRegistry.createTask('Starting Inference server', 'loading', labels);
if (!inferenceServer) {
const inferenceContainerId = await this.inferenceManager.createInferenceServer(
await withDefaultConfiguration({
modelsInfo: [options.model],
}),
);
inferenceServer = this.inferenceManager.get(inferenceContainerId);
this.taskRegistry.updateTask({
...task,
labels: {
...task.labels,
containerId: inferenceContainerId,
},
});
} else if (inferenceServer.status === 'stopped') {
await this.inferenceManager.startInferenceServer(inferenceServer.container.containerId);
}
task.state = 'success';
} catch (e) {
// we only skip the task update if the error is that we do not support this backend.
// If so, we build the image for the model service
if (task && String(e) !== 'no enabled provider could be found.') {
task.state = 'error';
task.error = `Something went wrong while starting the inference server: ${String(e)}`;
throw e;
}
} finally {
if (task) {
this.taskRegistry.updateTask(task);
}
}
}
}
// load and parse the recipe configuration file and filter containers based on architecture
const configAndFilteredContainers = this.getConfigAndFilterContainers(
recipe.basedir,
options.recipe.basedir,
localFolder,
!!inferenceServer,
{
...labels,
'recipe-id': recipe.id,
'recipe-id': options.recipe.id,
},
);
const images = await this.builderManager.build(
connection,
recipe,
options.connection,
options.recipe,
configAndFilteredContainers.containers,
configAndFilteredContainers.aiConfigFile.path,
{
...labels,
'recipe-id': recipe.id,
'recipe-id': options.recipe.id,
},
);

View File

@ -0,0 +1,39 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { ContainerProviderConnection } from '@podman-desktop/api';
import type { ModelInfo } from '@shared/models/IModelInfo';
import type { Recipe, RecipeDependencies } from '@shared/models/IRecipe';
export type ApplicationOptions = ApplicationOptionsDefault | ApplicationOptionsWithModelInference;
export interface ApplicationOptionsDefault {
connection: ContainerProviderConnection;
recipe: Recipe;
dependencies?: RecipeDependencies;
}
export type ApplicationOptionsWithModelInference = ApplicationOptionsDefault & {
model: ModelInfo;
};
export function isApplicationOptionsWithModelInference(
options: ApplicationOptions,
): options is ApplicationOptionsWithModelInference {
return 'model' in options;
}

View File

@ -170,36 +170,42 @@ beforeEach(async () => {
} as unknown as EventEmitter<unknown>);
});
test('expect requestPullApplication to provide a tracking id', async () => {
const connectionMock = {
name: 'Podman machine',
} as unknown as ContainerProviderConnection;
vi.mocked(podmanConnectionMock.findRunningContainerProviderConnection).mockReturnValue(connectionMock);
vi.spyOn(catalogManager, 'getRecipes').mockReturnValue([
{
id: 'recipe 1',
} as unknown as Recipe,
]);
vi.spyOn(catalogManager, 'getModelById').mockReturnValue({
id: 'model 1',
} as unknown as ModelInfo);
vi.mocked(applicationManager.requestPullApplication).mockResolvedValue('dummy-tracker');
const trackingId = await studioApiImpl.requestPullApplication({
modelId: 'model1',
recipeId: 'recipe 1',
});
expect(applicationManager.requestPullApplication).toHaveBeenCalledWith(
connectionMock,
expect.objectContaining({
id: 'recipe 1',
}),
expect.objectContaining({
describe.each([true, false])('with model is %o', withModel => {
test('expect requestPullApplication to provide a tracking id', async () => {
const connectionMock = {
name: 'Podman machine',
} as unknown as ContainerProviderConnection;
vi.mocked(podmanConnectionMock.findRunningContainerProviderConnection).mockReturnValue(connectionMock);
vi.spyOn(catalogManager, 'getRecipes').mockReturnValue([
{
id: 'recipe 1',
} as unknown as Recipe,
]);
vi.spyOn(catalogManager, 'getModelById').mockReturnValue({
id: 'model 1',
}),
);
expect(trackingId).toBe('dummy-tracker');
} as unknown as ModelInfo);
vi.mocked(applicationManager.requestPullApplication).mockResolvedValue('dummy-tracker');
const recipeId = 'recipe 1';
let modelId: string | undefined;
if (withModel) {
modelId = 'model1';
}
const trackingId = await studioApiImpl.requestPullApplication(withModel ? { recipeId, modelId } : { recipeId });
expect(applicationManager.requestPullApplication).toHaveBeenCalledWith({
connection: connectionMock,
recipe: expect.objectContaining({
id: 'recipe 1',
}),
model: withModel
? expect.objectContaining({
id: 'model 1',
})
: undefined,
});
expect(trackingId).toBe('dummy-tracker');
});
});
test('requestRemoveApplication should ask confirmation', async () => {

View File

@ -30,7 +30,7 @@ import type { TaskRegistry } from './registries/TaskRegistry';
import type { LocalRepository } from '@shared/models/ILocalRepository';
import type { LocalRepositoryRegistry } from './registries/LocalRepositoryRegistry';
import path from 'node:path';
import type { InferenceServer } from '@shared/models/IInference';
import type { InferenceServer, InferenceType } from '@shared/models/IInference';
import type { CreationInferenceServerOptions } from '@shared/models/InferenceServerConfig';
import type { InferenceManager } from './managers/inference/inferenceManager';
import type { Conversation } from '@shared/models/IPlaygroundMessage';
@ -53,10 +53,11 @@ import type { ExtensionConfiguration } from '@shared/models/IExtensionConfigurat
import type { ConfigurationRegistry } from './registries/ConfigurationRegistry';
import type { RecipeManager } from './managers/recipes/RecipeManager';
import type { PodmanConnection } from './managers/podmanConnection';
import type { RecipePullOptions } from '@shared/models/IRecipe';
import { isRecipePullOptionsWithModelInference, type RecipePullOptions } from '@shared/models/IRecipe';
import type { ContainerProviderConnection } from '@podman-desktop/api';
import type { NavigationRegistry } from './registries/NavigationRegistry';
import type { FilterRecipesResult, RecipeFilters } from '@shared/models/FilterRecipesResult';
import type { ApplicationOptions } from './models/ApplicationOptions';
interface PortQuickPickItem extends podmanDesktopApi.QuickPickItem {
port: number;
@ -143,6 +144,10 @@ export class StudioApiImpl implements StudioAPI {
return this.inferenceManager.getServers();
}
async getRegisteredProviders(): Promise<InferenceType[]> {
return this.inferenceManager.getRegisteredProviders();
}
async requestDeleteInferenceServer(...containerIds: string[]): Promise<void> {
// Do not wait on the promise as the api would probably timeout before the user answer.
if (containerIds.length === 0) throw new Error('At least one container id should be provided.');
@ -229,8 +234,6 @@ export class StudioApiImpl implements StudioAPI {
const recipe = this.catalogManager.getRecipes().find(recipe => recipe.id === options.recipeId);
if (!recipe) throw new Error(`recipe with if ${options.recipeId} not found`);
const model = this.catalogManager.getModelById(options.modelId);
let connection: ContainerProviderConnection | undefined = undefined;
if (options.connection) {
connection = this.podmanConnection.getContainerProviderConnection(options.connection);
@ -240,7 +243,25 @@ export class StudioApiImpl implements StudioAPI {
if (!connection) throw new Error('no running container provider connection found.');
return this.applicationManager.requestPullApplication(connection, recipe, model);
let model: ModelInfo | undefined;
let opts: ApplicationOptions;
if (isRecipePullOptionsWithModelInference(options)) {
model = this.catalogManager.getModelById(options.modelId);
opts = {
connection,
recipe,
dependencies: options.dependencies,
model,
};
} else {
opts = {
connection,
recipe,
dependencies: options.dependencies,
};
}
return this.applicationManager.requestPullApplication(opts);
}
async getModelsInfo(): Promise<ModelInfo[]> {

View File

@ -21,10 +21,12 @@
import { afterEach, beforeEach, expect, test, vi, describe, type MockInstance } from 'vitest';
import { Studio } from './studio';
import { type ExtensionContext, EventEmitter, version } from '@podman-desktop/api';
import { CatalogManager } from './managers/catalogManager';
import * as fs from 'node:fs';
vi.mock('./managers/modelsManager');
vi.mock('./managers/catalogManager');
const mockedExtensionContext = {
subscriptions: [],
@ -124,6 +126,12 @@ beforeEach(() => {
} as unknown as EventEmitter<unknown>);
mocks.postMessage.mockResolvedValue(undefined);
vi.mocked(CatalogManager).mockReturnValue({
onUpdate: vi.fn(),
init: vi.fn(),
getRecipes: vi.fn().mockReturnValue([]),
} as unknown as CatalogManager);
});
afterEach(() => {

View File

@ -212,7 +212,7 @@ export class Studio {
* Create catalog manager, responsible for loading the catalog files and watching for changes
*/
this.#catalogManager = new CatalogManager(this.#rpcExtension, appUserDirectory);
this.#catalogManager.init();
await this.#catalogManager.init();
/**
* The builder manager is handling the building tasks, create corresponding tasks
@ -251,7 +251,7 @@ export class Studio {
const hfModelHandler = new HuggingFaceModelHandler(this.#modelsManager);
this.#extensionContext.subscriptions.push(hfModelHandler);
this.#extensionContext.subscriptions.push(modelHandlerRegistry.register(hfModelHandler));
this.#modelsManager.init();
await this.#modelsManager.init();
this.#extensionContext.subscriptions.push(this.#modelsManager);
/**
@ -358,6 +358,7 @@ export class Studio {
this.#telemetry,
this.#podManager,
this.#recipeManager,
this.#llamaStackManager,
);
this.#applicationManager.init();
this.#extensionContext.subscriptions.push(this.#applicationManager);

View File

@ -1,7 +1,7 @@
{
"name": "frontend-app",
"displayName": "UI for AI Lab",
"version": "1.7.0-next",
"version": "1.9.0-next",
"type": "module",
"license": "Apache-2.0",
"scripts": {
@ -18,32 +18,32 @@
"@fortawesome/free-regular-svg-icons": "^6.7.2",
"@podman-desktop/ui-svelte": "1.16.0-202501131429-9076680fda2",
"tinro": "^0.6.12",
"filesize": "^10.1.6",
"filesize": "^11.0.2",
"humanize-duration": "^3.32.2",
"moment": "^2.30.1",
"semver": "^7.7.2"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "5.0.3",
"@sveltejs/vite-plugin-svelte": "5.1.0",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.7",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@tailwindcss/vite": "^4.1.12",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/svelte": "^5.2.8",
"@testing-library/user-event": "^14.6.1",
"@tsconfig/svelte": "^5.0.4",
"@tsconfig/svelte": "^5.0.5",
"@types/humanize-duration": "^3.27.4",
"@typescript-eslint/eslint-plugin": "8.32.1",
"@typescript-eslint/eslint-plugin": "8.40.0",
"jsdom": "^26.1.0",
"monaco-editor": "^0.52.2",
"postcss": "^8.5.3",
"postcss": "^8.5.6",
"postcss-load-config": "^6.0.1",
"svelte": "5.31.0",
"svelte": "5.38.2",
"svelte-fa": "^4.0.4",
"svelte-select": "^5.8.3",
"svelte-markdown": "^0.4.1",
"svelte-preprocess": "^6.0.3",
"tailwindcss": "^4.1.7",
"tailwindcss": "^4.1.12",
"vitest": "^3.0.5"
}
}

View File

@ -117,7 +117,7 @@ function toggleExpanded(): void {
class="w-full flex flex-row gap-2 py-2"
class:overflow-hidden={!expanded}
class:flex-wrap={expanded}>
{#each TAGS as tag, i (tag)}
{#each TAGS as tag, i (i)}
<div bind:this={divTags[i]}>
<Badge class="{getBGColor(tag)} {getTextColor(tag)}" content={updateContent(tag)} />
</div>

View File

@ -21,8 +21,8 @@ import { gte } from 'semver';
const USE_CASES = ['natural-language-processing', 'audio', 'computer-vision'];
const LANGUAGES = ['java', 'javascript', 'python'];
export const FRAMEWORKS = ['langchain', 'langchain4j', 'quarkus', 'react', 'streamlit', 'vectordb'];
export const TOOLS = ['none', 'llama-cpp', 'whisper-cpp'];
export const FRAMEWORKS = ['langchain', 'langchain4j', 'quarkus', 'react', 'streamlit', 'vectordb', 'llama-stack-sdk'];
export const TOOLS = ['none', 'llama-cpp', 'whisper-cpp', 'llama-stack'];
// Defaulting to Podman Desktop min version we need to run
let version: string = '1.8.0';

View File

@ -0,0 +1,99 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import '@testing-library/jest-dom/vitest';
import { beforeEach, vi, test, expect } from 'vitest';
import { render, fireEvent, within } from '@testing-library/svelte';
import InferenceRuntimeSelect from '/@/lib/select/InferenceRuntimeSelect.svelte';
import { InferenceType } from '@shared/models/IInference';
const providers: InferenceType[] = [InferenceType.LLAMA_CPP, InferenceType.OPENVINO, InferenceType.WHISPER_CPP];
beforeEach(() => {
// mock scrollIntoView
window.HTMLElement.prototype.scrollIntoView = vi.fn();
});
test('Lists all runtime options', async () => {
const { container } = render(InferenceRuntimeSelect, {
value: undefined,
providers,
disabled: false,
});
const input = within(container).getByLabelText('Select Inference Runtime');
await fireEvent.pointerUp(input);
const items = container.querySelectorAll('div[class~="list-item"]');
const expectedOptions = providers;
expect(items.length).toBe(expectedOptions.length);
expectedOptions.forEach((option, i) => {
expect(items[i]).toHaveTextContent(option);
});
});
test('Selected value should be visible', async () => {
const { container } = render(InferenceRuntimeSelect, {
value: undefined,
providers,
disabled: false,
});
const input = within(container).getByLabelText('Select Inference Runtime');
await fireEvent.pointerUp(input);
const items = container.querySelectorAll('div[class~="list-item"]');
const expectedOptions = providers;
await fireEvent.click(items[0]);
const valueContainer = container.querySelector('.value-container');
if (!(valueContainer instanceof HTMLElement)) throw new Error('Missing value container');
const selectedLabel = within(valueContainer).getByText(expectedOptions[0]);
expect(selectedLabel).toBeDefined();
});
test('Exclude specific runtime from list', async () => {
const excluded = [InferenceType.WHISPER_CPP, InferenceType.OPENVINO];
const { container } = render(InferenceRuntimeSelect, {
value: undefined,
providers,
disabled: false,
exclude: excluded,
});
const input = within(container).getByLabelText('Select Inference Runtime');
await fireEvent.pointerUp(input);
const items = container.querySelectorAll('div[class~="list-item"]');
const itemTexts = Array.from(items).map(item => item.textContent?.trim());
excluded.forEach(excludedType => {
expect(itemTexts).not.toContain(excludedType);
});
const expected = providers.filter(type => !excluded.includes(type));
expected.forEach(included => {
expect(itemTexts).toContain(included);
});
});

View File

@ -0,0 +1,34 @@
<script lang="ts">
import Select from '/@/lib/select/Select.svelte';
import type { InferenceType } from '@shared/models/IInference';
interface Props {
disabled?: boolean;
value: InferenceType | undefined;
providers: InferenceType[];
exclude?: InferenceType[];
}
let { value = $bindable(), disabled, providers, exclude = [] }: Props = $props();
// Filter options based on optional exclude list
const options = $derived(() =>
providers.filter(type => !exclude.includes(type)).map(type => ({ value: type, label: type })),
);
function handleOnChange(nValue: { value: string } | undefined): void {
if (nValue) {
value = nValue.value as InferenceType;
} else {
value = undefined;
}
}
</script>
<Select
label="Select Inference Runtime"
name="select-inference-runtime"
disabled={disabled}
value={value ? { label: value, value: value } : undefined}
onchange={handleOnChange}
placeholder="Select Inference Runtime to use"
items={options()} />

View File

@ -421,3 +421,47 @@ test('model-id query should be used to select default model', async () => {
});
});
});
test('models with backend "none" should be filtered out', async () => {
const modelsInfoList = writable<ModelInfo[]>([
{
id: 'model-valid',
name: 'Valid Model',
description: 'A model with a valid backend',
backend: 'llama-cpp',
file: {
file: 'file',
path: '/valid-path',
},
} as unknown as ModelInfo,
{
id: 'model-none',
name: 'None Backend Model',
description: 'A model with backend none',
backend: 'none',
file: {
file: 'file',
path: '/none-path',
},
} as unknown as ModelInfo,
]);
vi.mocked(ModelsInfoStore).modelsInfo = modelsInfoList;
router.location.query.set('model-id', 'model-valid');
render(CreateService);
expect(screen.queryByText('None Backend Model')).toBeNull();
const createBtn = screen.getByTitle('Create service');
await vi.waitFor(() => {
expect(createBtn).toBeEnabled();
});
await fireEvent.click(createBtn);
expect(vi.mocked(studioClient.requestCreateInferenceServer)).toHaveBeenCalledWith(
expect.objectContaining({
modelsInfo: [expect.objectContaining({ id: 'model-valid' })],
}),
);
});

View File

@ -25,8 +25,8 @@ interface Props {
let { trackingId }: Props = $props();
// List of the models available locally
let localModels: ModelInfo[] = $derived($modelsInfo.filter(model => model.file));
// List of the models available locally exlude models with none backend
let localModels: ModelInfo[] = $derived($modelsInfo.filter(model => model.file && model.backend !== 'none'));
// The container provider connection to use
let containerProviderConnection: ContainerProviderConnectionInfo | undefined = $state(undefined);

View File

@ -16,6 +16,7 @@ import { tasks } from '/@/stores/tasks';
import ModelStatusIcon from '../lib/icons/ModelStatusIcon.svelte';
import { router } from 'tinro';
import { faBookOpen, faFileImport } from '@fortawesome/free-solid-svg-icons';
import { SvelteSet } from 'svelte/reactivity';
const columns = [
new TableColumn<ModelInfo>('Status', {
@ -24,21 +25,21 @@ const columns = [
comparator: (a, b): number => (a.file ? 0 : 1) - (b.file ? 0 : 1),
}),
new TableColumn<ModelInfo>('Name', {
width: '3fr',
width: 'minmax(100px,1fr)',
renderer: ModelColumnName,
comparator: (a, b): number => b.name.localeCompare(a.name),
}),
new TableColumn<ModelInfo>('Size', {
width: '50px',
width: 'minmax(10px,50px)',
renderer: ModelColumnSize,
comparator: (a, b): number => (a.file?.size ?? 0) - (b.file?.size ?? 0),
}),
new TableColumn<ModelInfo>('Age', {
width: '70px',
width: 'minmax(10px,70px)',
renderer: ModelColumnAge,
comparator: (a, b): number => (a.file?.creation?.getTime() ?? 0) - (b.file?.creation?.getTime() ?? 0),
}),
new TableColumn<ModelInfo>('', { width: '225px', align: 'right', renderer: ModelColumnLabels }),
new TableColumn<ModelInfo>('', { width: 'minmax(50px,175px)', align: 'right', renderer: ModelColumnLabels }),
new TableColumn<ModelInfo>('Actions', { align: 'right', width: '120px', renderer: ModelColumnActions }),
];
const row = new TableRow<ModelInfo>({});
@ -70,7 +71,7 @@ onMount(() => {
// Subscribe to the tasks store
const tasksUnsubscribe = tasks.subscribe(value => {
// Filter out duplicates
const modelIds = new Set<string>();
const modelIds = new SvelteSet<string>();
pullingTasks = value.reduce((filtered: Task[], task: Task) => {
if (
(task.state === 'loading' || task.state === 'error') &&

View File

@ -55,11 +55,24 @@ const dummyWhisperCppModel: ModelInfo = {
backend: InferenceType.WHISPER_CPP,
};
const dummyOpenVinoModel: ModelInfo = {
id: 'openvino-model-id',
name: 'Dummy Openvino model',
file: {
file: 'file',
path: path.resolve(os.tmpdir(), 'path'),
},
properties: {},
description: '',
backend: InferenceType.OPENVINO,
};
vi.mock('../utils/client', async () => {
return {
studioClient: {
requestCreatePlayground: vi.fn(),
getExtensionConfiguration: vi.fn().mockResolvedValue({}),
getRegisteredProviders: vi.fn().mockResolvedValue([]),
},
rpcBrowser: {
subscribe: (): unknown => {
@ -88,28 +101,58 @@ beforeEach(() => {
const tasksList = writable<Task[]>([]);
vi.mocked(tasksStore).tasks = tasksList;
vi.mocked(studioClient.getRegisteredProviders).mockResolvedValue([
InferenceType.LLAMA_CPP,
InferenceType.WHISPER_CPP,
InferenceType.OPENVINO,
]);
});
test('model should be selected by default', () => {
test('model should be selected by default when runtime is set', async () => {
const modelsInfoList = writable<ModelInfo[]>([dummyLlamaCppModel]);
vi.mocked(modelsInfoStore).modelsInfo = modelsInfoList;
vi.mocked(studioClient.requestCreatePlayground).mockRejectedValue('error creating playground');
const { container } = render(PlaygroundCreate);
const { container } = render(PlaygroundCreate, { props: { exclude: [InferenceType.NONE] } });
// Select our runtime
const dropdown = within(container).getByLabelText('Select Inference Runtime');
await userEvent.click(dropdown);
const llamacppOption = within(container).getByText(InferenceType.LLAMA_CPP);
await userEvent.click(llamacppOption);
const model = within(container).getByText(dummyLlamaCppModel.name);
expect(model).toBeInTheDocument();
});
test('models with incompatible backend should not be listed', async () => {
const modelsInfoList = writable<ModelInfo[]>([dummyWhisperCppModel]);
test('selecting a runtime filters the displayed models', async () => {
const modelsInfoList = writable<ModelInfo[]>([dummyLlamaCppModel, dummyWhisperCppModel, dummyOpenVinoModel]);
vi.mocked(modelsInfoStore).modelsInfo = modelsInfoList;
const { container } = render(PlaygroundCreate, { props: { exclude: [InferenceType.NONE] } });
// Select our runtime
const dropdown = within(container).getByLabelText('Select Inference Runtime');
await userEvent.click(dropdown);
const openvinoOption = within(container).getByText(InferenceType.OPENVINO);
await userEvent.click(openvinoOption);
expect(within(container).queryByText(dummyOpenVinoModel.name)).toBeInTheDocument();
expect(within(container).queryByText(dummyLlamaCppModel.name)).toBeNull();
expect(within(container).queryByText(dummyWhisperCppModel.name)).toBeNull();
});
test('should show warning when no local models are available', () => {
const modelsInfoList = writable<ModelInfo[]>([]);
vi.mocked(modelsInfoStore).modelsInfo = modelsInfoList;
const { container } = render(PlaygroundCreate);
const model = within(container).queryByText(dummyWhisperCppModel.name);
expect(model).toBeNull();
const warning = within(container).getByText(/You don't have any models downloaded/);
expect(warning).toBeInTheDocument();
});
test('should display error message if createPlayground fails', async () => {
@ -123,6 +166,13 @@ test('should display error message if createPlayground fails', async () => {
const errorMessage = within(container).queryByLabelText('Error Message Content');
expect(errorMessage).not.toBeInTheDocument();
// Select the runtime first
const runtimeDropdown = within(container).getByLabelText('Select Inference Runtime');
await userEvent.click(runtimeDropdown);
const runtimeOption = within(container).getByText(InferenceType.LLAMA_CPP);
await userEvent.click(runtimeOption);
const createButton = within(container).getByTitle('Create playground');
await userEvent.click(createButton);

View File

@ -14,9 +14,34 @@ import type { Unsubscriber } from 'svelte/store';
import { Button, ErrorMessage, FormPage, Input } from '@podman-desktop/ui-svelte';
import ModelSelect from '/@/lib/select/ModelSelect.svelte';
import { InferenceType } from '@shared/models/IInference';
import InferenceRuntimeSelect from '/@/lib/select/InferenceRuntimeSelect.svelte';
import { configuration } from '../stores/extensionConfiguration';
// Get recommended runtime
let runtime: InferenceType | undefined = undefined;
// Exlude certain runtimes from selection
export let exclude: InferenceType[] = [InferenceType.NONE, InferenceType.WHISPER_CPP];
// Get registered list of providers
let providers: InferenceType[] = [];
onMount(async () => {
providers = await studioClient.getRegisteredProviders();
const inferenceRuntime = $configuration?.inferenceRuntime;
if (
Object.values(InferenceType).includes(inferenceRuntime as InferenceType) &&
!exclude.includes(inferenceRuntime as InferenceType)
) {
runtime = inferenceRuntime as InferenceType;
}
});
let localModels: ModelInfo[];
$: localModels = $modelsInfo.filter(model => model.file && model.backend !== InferenceType.WHISPER_CPP);
$: localModels = $modelsInfo.filter(
model => model.file && (!runtime || model.backend === runtime) && !exclude.includes(model.backend as InferenceType),
);
$: availModels = $modelsInfo.filter(model => !model.file);
let model: ModelInfo | undefined = undefined;
let submitted: boolean = false;
@ -30,10 +55,11 @@ let trackingId: string | undefined = undefined;
// The trackedTasks are the tasks linked to the trackingId
let trackedTasks: Task[] = [];
$: {
if (!model && localModels.length > 0) {
model = localModels[0];
}
// Preset model selection depending on runtime
$: if (localModels.length > 0) {
model = localModels[0];
} else {
model = undefined;
}
function openModelsPage(): void {
@ -145,6 +171,12 @@ export function goToUpPage(): void {
placeholder="Leave blank to generate a name"
aria-label="playgroundName" />
<!-- inference runtime -->
<label for="inference-runtime" class="pt-4 block mb-2 font-bold text-[var(--pd-content-card-header-text)]">
Inference Runtime
</label>
<InferenceRuntimeSelect bind:value={runtime} providers={providers} exclude={exclude} />
<!-- model input -->
<label for="model" class="pt-4 block mb-2 font-bold text-[var(--pd-content-card-header-text)]">Model</label>
<ModelSelect models={localModels} disabled={submitted} bind:value={model} />

View File

@ -10,6 +10,7 @@ import { studioClient } from '../utils/client';
import type { CatalogFilterKey, Choice, RecipeChoices, RecipeFilters } from '@shared/models/FilterRecipesResult';
import { onMount } from 'svelte';
import { configuration } from '../stores/extensionConfiguration';
import { SvelteMap } from 'svelte/reactivity';
// filters available in the dropdowns for the user to select
let choices: RecipeChoices = $state({});
@ -53,7 +54,7 @@ let groups: Map<Category, Recipe[]> = $derived.by(() => {
if (!Object.keys(categoryDict).length) {
return new Map();
}
const output: Map<Category, Recipe[]> = new Map();
const output: Map<Category, Recipe[]> = new SvelteMap();
for (const recipe of recipes) {
if (recipe.categories.length === 0) {
output.set(UNCLASSIFIED, [...(output.get(UNCLASSIFIED) ?? []), recipe]);

View File

@ -69,6 +69,14 @@ const fakeRecipe: Recipe = {
categories: [],
} as unknown as Recipe;
const fakeLlamaStackRecipe: Recipe = {
id: 'dummy-llama-stack-recipe-id',
backend: 'llama-stack',
name: 'Dummy Llama Stack Recipe',
description: 'Dummy description',
categories: [],
} as unknown as Recipe;
const fakeRecommendedModel: ModelInfo = {
id: 'dummy-model-1',
backend: InferenceType.LLAMA_CPP,
@ -100,7 +108,7 @@ beforeEach(() => {
router.location.query.clear();
vi.mocked(CatalogStore).catalog = readable<ApplicationCatalog>({
recipes: [fakeRecipe],
recipes: [fakeRecipe, fakeLlamaStackRecipe],
models: [],
categories: [],
version: '',
@ -147,7 +155,7 @@ test('Recipe Local Repository should be visible when defined', async () => {
expect(span.textContent).toBe('dummy-recipe-path');
});
test('Submit button should be disabled when no model is selected', async () => {
test('Submit button should be disabled when model is required and no model is selected', async () => {
vi.mocked(ModelsInfoStore).modelsInfo = readable([]);
render(StartRecipe, {
@ -159,6 +167,18 @@ test('Submit button should be disabled when no model is selected', async () => {
expect(button).toBeDisabled();
});
test('Submit button should be enabled when model is not required', async () => {
vi.mocked(ModelsInfoStore).modelsInfo = readable([]);
render(StartRecipe, {
recipeId: 'dummy-llama-stack-recipe-id',
});
const button = screen.getByTitle(`Start ${fakeLlamaStackRecipe.name} recipe`);
expect(button).toBeDefined();
expect(button).toBeEnabled();
});
test('First recommended model should be selected as default model', async () => {
const { container } = render(StartRecipe, {
recipeId: 'dummy-recipe-id',
@ -265,6 +285,29 @@ test('Submit button should call requestPullApplication with proper arguments', a
connection: containerProviderConnection,
recipeId: fakeRecipe.id,
modelId: fakeRecommendedModel.id,
dependencies: {
llamaStack: false,
},
});
});
});
test('Submit button should call requestPullApplication with proper arguments for llama-stack recipe', async () => {
render(StartRecipe, {
recipeId: 'dummy-llama-stack-recipe-id',
});
const button = screen.getByTitle(`Start ${fakeLlamaStackRecipe.name} recipe`);
expect(button).toBeEnabled();
await fireEvent.click(button);
await vi.waitFor(() => {
expect(studioClient.requestPullApplication).toHaveBeenCalledWith({
connection: containerProviderConnection,
recipeId: fakeLlamaStackRecipe.id,
dependencies: {
llamaStack: true,
},
});
});
});

View File

@ -2,7 +2,7 @@
import { faFolder, faRocket, faUpRightFromSquare, faWarning } from '@fortawesome/free-solid-svg-icons';
import { catalog } from '/@/stores/catalog';
import Fa from 'svelte-fa';
import type { Recipe } from '@shared/models/IRecipe';
import type { Recipe, RecipePullOptions, RecipePullOptionsWithModelInference } from '@shared/models/IRecipe';
import type { LocalRepository } from '@shared/models/ILocalRepository';
import { findLocalRepositoryByRecipeId } from '/@/utils/localRepositoriesUtils';
import { localRepositories } from '/@/stores/localRepositories';
@ -53,6 +53,16 @@ let completed: boolean = $state(false);
let errorMsg: string | undefined = $state(undefined);
let formValid = $derived.by<boolean>((): boolean => {
if (!recipe) {
return false;
}
if (!isModelNeeded(recipe)) {
return true;
}
return !!model;
});
$effect(() => {
// Select default connection
if (!containerProviderConnection && startedContainerProviderConnectionInfo.length > 0) {
@ -100,16 +110,22 @@ function populateModelFromTasks(trackedTasks: Task[]): void {
}
async function submit(): Promise<void> {
if (!recipe || !model) return;
if (!recipe || !formValid) return;
errorMsg = undefined;
try {
const trackingId = await studioClient.requestPullApplication({
const options: RecipePullOptions = {
recipeId: $state.snapshot(recipe.id),
modelId: $state.snapshot(model.id),
connection: $state.snapshot(containerProviderConnection),
});
dependencies: {
llamaStack: recipe.backend === 'llama-stack',
},
};
if (model) {
(options as RecipePullOptionsWithModelInference).modelId = $state.snapshot(model.id);
}
const trackingId = await studioClient.requestPullApplication(options);
router.location.query.set('trackingId', trackingId);
} catch (err: unknown) {
console.error('Something wrong while trying to create the inference server.', err);
@ -124,6 +140,10 @@ export function goToUpPage(): void {
function handleOnClick(): void {
router.goto(`/recipe/${recipeId}/running`);
}
function isModelNeeded(recipe: Recipe): boolean {
return recipe.backend !== 'llama-stack';
}
</script>
<FormPage
@ -183,17 +203,18 @@ function handleOnClick(): void {
bind:value={containerProviderConnection}
containerProviderConnections={startedContainerProviderConnectionInfo} />
{/if}
<!-- model form -->
<label for="select-model" class="pt-4 block mb-2 font-bold text-[var(--pd-content-card-header-text)]"
>Model</label>
<ModelSelect bind:value={model} disabled={loading} recommended={recipe.recommended} models={models} />
{#if model && model.file === undefined}
<div class="text-gray-800 text-sm flex items-center">
<Fa class="mr-2" icon={faWarning} />
<span role="alert"
>The selected model will be downloaded. This action can take some time depending on your connection</span>
</div>
{#if isModelNeeded(recipe)}
<!-- model form -->
<label for="select-model" class="pt-4 block mb-2 font-bold text-[var(--pd-content-card-header-text)]"
>Model</label>
<ModelSelect bind:value={model} disabled={loading} recommended={recipe.recommended} models={models} />
{#if model && model.file === undefined}
<div class="text-gray-800 text-sm flex items-center">
<Fa class="mr-2" icon={faWarning} />
<span role="alert"
>The selected model will be downloaded. This action can take some time depending on your connection</span>
</div>
{/if}
{/if}
</div>
@ -209,7 +230,7 @@ function handleOnClick(): void {
title="Start {recipe.name} recipe"
inProgress={loading}
on:click={submit}
disabled={!model || loading || !containerProviderConnection}
disabled={!formValid || loading || !containerProviderConnection}
icon={faRocket}>
Start {recipe.name} recipe
</Button>

View File

@ -98,8 +98,7 @@ test('Instructions block should be displayed if Llama Stack container is found',
});
render(StartLlamaStackContainer);
await tick();
screen.getByText('Instructions');
await vi.waitFor(() => screen.getByText('Instructions'));
});
test('start button should be displayed and enabled', async () => {

View File

@ -17,6 +17,7 @@
***********************************************************************/
import type { ModelInfo } from './models/IModelInfo';
import type { InferenceType } from '@shared/models/IInference';
import type { ApplicationCatalog } from './models/IApplicationCatalog';
import type { OpenDialogOptions, Uri } from '@podman-desktop/api';
import type { ApplicationState } from './models/IApplicationState';
@ -121,6 +122,11 @@ export interface StudioAPI {
*/
getInferenceServers(): Promise<InferenceServer[]>;
/**
* Get inference providers
*/
getRegisteredProviders(): Promise<InferenceType[]>;
/**
* Request to start an inference server
* @param options The options to use

View File

@ -19,10 +19,26 @@ import type { ContainerProviderConnectionInfo } from './IContainerConnectionInfo
import type { InferenceServer } from './IInference';
export interface RecipePullOptions {
export type RecipePullOptions = RecipePullOptionsDefault | RecipePullOptionsWithModelInference;
export interface RecipePullOptionsDefault {
connection?: ContainerProviderConnectionInfo;
recipeId: string;
dependencies?: RecipeDependencies;
}
export type RecipePullOptionsWithModelInference = RecipePullOptionsDefault & {
modelId: string;
};
export interface RecipeDependencies {
llamaStack?: boolean;
}
export function isRecipePullOptionsWithModelInference(
options: RecipePullOptions,
): options is RecipePullOptionsWithModelInference {
return 'modelId' in options;
}
export interface RecipeComponents {

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,19 @@
{
"name": "ai-lab-tests-playwright",
"version": "1.7.0-next",
"version": "1.9.0-next",
"description": "Podman Desktop AI Lab extension Playwright E2E tests",
"scripts": {
"test:e2e": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/",
"test:e2e:smoke": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/ -g @smoke"
"test:e2e:smoke": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/ -g @smoke",
"test:e2e:instructlab": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' -- npx playwright test src/ -g @instructlab"
},
"author": "Red Hat",
"license": "Apache-2.0",
"devDependencies": {
"@playwright/test": "^1.52.0",
"@podman-desktop/tests-playwright": "1.18.1",
"@playwright/test": "^1.55.0",
"@podman-desktop/tests-playwright": "1.21.0",
"@types/node": "^22",
"electron": "^36.2.1",
"typescript": "^5.8.3",
"vitest": "^3.0.5",
"typescript": "^5.9.2",
"xvfb-maybe": "^0.2.1"
},
"type": "module"

View File

@ -21,6 +21,7 @@ import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
outputDir: './output/',
workers: 1,
timeout: 60_000,
reporter: [
['list'],

Binary file not shown.

View File

@ -16,39 +16,84 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { Locator, Page } from '@playwright/test';
/**
* The 'test-audio-to-text.wav' file used in this test was sourced from the
* whisper.cpp project (https://github.com/ggml-org/whisper.cpp).
* It is licensed under the MIT License (see https://github.com/ggml-org/whisper.cpp/blob/master/LICENSE for details).
* This specific WAV file is used solely for Playwright testing purposes within this repository.
*/
import type { APIResponse, Locator } from '@playwright/test';
import type { NavigationBar, ExtensionsPage } from '@podman-desktop/tests-playwright';
import {
ContainerDetailsPage,
ContainerState,
expect as playExpect,
test,
RunnerOptions,
isWindows,
waitForPodmanMachineStartup,
isLinux,
isMac,
isCI,
resetPodmanMachinesFromCLI,
} from '@podman-desktop/tests-playwright';
import { AILabPage } from './model/ai-lab-page';
import type { AILabDashboardPage } from './model/ai-lab-dashboard-page';
import type { AILabRecipesCatalogPage } from './model/ai-lab-recipes-catalog-page';
import { AILabExtensionDetailsPage } from './model/podman-extension-ai-lab-details-page';
import type { AILabCatalogPage } from './model/ai-lab-catalog-page';
import { handleWebview } from './utils/webviewHandler';
import type { AILabServiceDetailsPage } from './model/ai-lab-service-details-page';
import type { AILabPlaygroundsPage } from './model/ai-lab-playgrounds-page';
import type { AILabPlaygroundDetailsPage } from './model/ai-lab-playground-details-page';
import {
getExtensionCard,
getExtensionVersion,
openAILabExtensionDetails,
openAILabPreferences,
reopenAILabDashboard,
waitForExtensionToInitialize,
} from './utils/aiLabHandler';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import type { AILabTryInstructLabPage } from './model/ai-lab-try-instructlab-page';
const AI_LAB_EXTENSION_OCI_IMAGE =
process.env.EXTENSION_OCI_IMAGE ?? 'ghcr.io/containers/podman-desktop-extension-ai-lab:nightly';
const AI_LAB_EXTENSION_PREINSTALLED: boolean = process.env.EXTENSION_PREINSTALLED === 'true';
const AI_LAB_CATALOG_EXTENSION_LABEL: string = 'redhat.ai-lab';
const AI_LAB_CATALOG_EXTENSION_NAME: string = 'Podman AI Lab extension';
const AI_LAB_CATALOG_STATUS_ACTIVE: string = 'ACTIVE';
let webview: Page;
let aiLabPage: AILabPage;
let aiLabPage: AILabDashboardPage;
const runnerOptions = {
customFolder: 'ai-lab-tests-pd',
aiLabModelUploadDisabled: isWindows ? true : false,
};
interface AiApp {
appName: string;
appModel: string;
}
const AI_APPS: AiApp[] = [
{ appName: 'Audio to Text', appModel: 'ggerganov/whisper.cpp' },
{ appName: 'ChatBot', appModel: 'ibm-granite/granite-3.3-8b-instruct-GGUF' },
{ appName: 'Summarizer', appModel: 'ibm-granite/granite-3.3-8b-instruct-GGUF' },
{ appName: 'Code Generation', appModel: 'ibm-granite/granite-3.3-8b-instruct-GGUF' },
{ appName: 'RAG Chatbot', appModel: 'ibm-granite/granite-3.3-8b-instruct-GGUF' },
{ appName: 'Function calling', appModel: 'ibm-granite/granite-3.3-8b-instruct-GGUF' },
{ appName: 'Object Detection', appModel: 'facebook/detr-resnet-101' },
];
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const TEST_AUDIO_FILE_PATH: string = path.resolve(
__dirname,
'..',
'..',
'playwright',
'resources',
`test-audio-to-text.wav`,
);
test.use({
runnerOptions: new RunnerOptions(runnerOptions),
});
@ -63,13 +108,13 @@ test.beforeAll(async ({ runner, welcomePage, page }) => {
});
test.afterAll(async ({ runner }) => {
test.setTimeout(120_000);
await cleanupServiceModels();
test.setTimeout(180_000);
await resetPodmanMachinesFromCLI();
await runner.close();
});
test.describe.serial(`AI Lab extension installation and verification`, () => {
test.describe.serial(`AI Lab extension installation`, { tag: '@smoke' }, () => {
test.describe.serial(`AI Lab extension installation`, { tag: ['@smoke', '@instructLab'] }, () => {
let extensionsPage: ExtensionsPage;
test(`Open Settings -> Extensions page`, async ({ navigationBar }) => {
@ -86,40 +131,63 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
});
test('Extension (card) is installed, present and active', async ({ navigationBar }) => {
const extensions = await navigationBar.openExtensions();
await playExpect
.poll(async () => await extensions.extensionIsInstalled(AI_LAB_CATALOG_EXTENSION_LABEL), { timeout: 30000 })
.toBeTruthy();
const extensionCard = await extensions.getInstalledExtension(
AI_LAB_CATALOG_EXTENSION_NAME,
AI_LAB_CATALOG_EXTENSION_LABEL,
);
await waitForExtensionToInitialize(navigationBar);
const extensionCard = await getExtensionCard(navigationBar);
await playExpect(extensionCard.status).toHaveText(AI_LAB_CATALOG_STATUS_ACTIVE);
});
test(`Extension's details show correct status, no error`, async ({ page, navigationBar }) => {
const extensions = await navigationBar.openExtensions();
const extensionCard = await extensions.getInstalledExtension('ai-lab', AI_LAB_CATALOG_EXTENSION_LABEL);
await extensionCard.openExtensionDetails(AI_LAB_CATALOG_EXTENSION_NAME);
const details = new AILabExtensionDetailsPage(page);
await playExpect(details.heading).toBeVisible();
await playExpect(details.status).toHaveText(AI_LAB_CATALOG_STATUS_ACTIVE);
const errorTab = details.tabs.getByRole('button', { name: 'Error' });
// we would like to propagate the error's stack trace into test failure message
let stackTrace = '';
if ((await errorTab.count()) > 0) {
await details.activateTab('Error');
stackTrace = await details.errorStackTrace.innerText();
}
await playExpect(errorTab, `Error Tab was present with stackTrace: ${stackTrace}`).not.toBeVisible();
test(`Extension's details show correct status, no error`, async ({ navigationBar }) => {
const aiLabExtensionDetailsPage = await openAILabExtensionDetails(navigationBar);
await aiLabExtensionDetailsPage.waitForLoad();
await aiLabExtensionDetailsPage.checkIsActive(AI_LAB_CATALOG_STATUS_ACTIVE);
await aiLabExtensionDetailsPage.checkForErrors();
});
test(`Verify AI Lab extension is installed`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
test(`Verify AI Lab is accessible`, async ({ runner, page, navigationBar }) => {
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
});
});
test.describe.serial(`AI Lab extension GPU preferences`, { tag: '@smoke' }, () => {
test(`Verify GPU support banner is visible, preferences are disabled`, async ({ page, navigationBar }) => {
test.setTimeout(15_000);
await playExpect(aiLabPage.gpuSupportBanner).toBeVisible();
await playExpect(aiLabPage.enableGpuButton).toBeVisible();
await playExpect(aiLabPage.dontDisplayButton).toBeVisible();
const preferencesPage = await openAILabPreferences(navigationBar, page);
await preferencesPage.waitForLoad();
playExpect(await preferencesPage.isGPUPreferenceEnabled()).toBeFalsy();
});
test(`Enable GPU support and verify preferences`, async ({ runner, page, navigationBar }) => {
test.setTimeout(30_000);
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.waitForLoad();
await aiLabPage.enableGpuSupport();
const preferencesPage = await openAILabPreferences(navigationBar, page);
await preferencesPage.waitForLoad();
playExpect(await preferencesPage.isGPUPreferenceEnabled()).toBeTruthy();
});
test.afterAll(
`Disable GPU support, return to AI Lab Dashboard and hide banner`,
async ({ runner, page, navigationBar }) => {
test.setTimeout(30_000);
const preferencesPage = await openAILabPreferences(navigationBar, page);
await preferencesPage.waitForLoad();
await preferencesPage.disableGPUPreference();
playExpect(await preferencesPage.isGPUPreferenceEnabled()).toBeFalsy();
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await playExpect(aiLabPage.gpuSupportBanner).toBeVisible();
await playExpect(aiLabPage.enableGpuButton).toBeVisible();
await playExpect(aiLabPage.dontDisplayButton).toBeVisible();
await aiLabPage.dontDisplayButton.click();
await playExpect(aiLabPage.gpuSupportBanner).toBeHidden();
},
);
});
test.describe.serial('AI Lab API endpoint e2e test', { tag: '@smoke' }, () => {
let localServerPort: string;
let extensionVersion: string | undefined;
@ -127,11 +195,8 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
test.beforeAll(
'Get AI Lab extension version and open AI Lab navigation bar',
async ({ page, runner, navigationBar }) => {
const extensions = await navigationBar.openExtensions();
extensionVersion = await extensions.getInstalledExtensionVersion('ai-lab', AI_LAB_CATALOG_EXTENSION_LABEL);
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
extensionVersion = await getExtensionVersion(navigationBar);
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
},
);
@ -160,8 +225,9 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
playExpect(apiResponse.version).toBe(extensionVersion);
});
test(`Download ${model} via API`, async ({ request }) => {
test.setTimeout(300_000);
// 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.setTimeout(610_000);
const catalogPage = await aiLabPage.navigationBar.openCatalog();
await catalogPage.waitForLoad();
console.log(`Downloading ${model}...`);
@ -175,17 +241,13 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
insecure: false,
stream: true,
},
timeout: 300_000,
timeout: 600_000,
});
const body = await response.body();
const text = body.toString();
playExpect(text).toContain('success');
});
// This test is currently failing due to a known issue: https://github.com/containers/podman-desktop-extension-ai-lab/issues/2925
test.fail(`Verify ${model} is available in AI Lab Catalog`, async () => {
const catalogPage = await aiLabPage.navigationBar.openCatalog();
await aiLabPage.navigationBar.openCatalog();
await catalogPage.waitForLoad();
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
@ -194,7 +256,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
test.fail(`Verify ${model} is listed in models fetched from API`, async ({ request }) => {
test.skip(`Verify ${model} is listed in models fetched from API`, async ({ request }) => {
const response = await request.get(`http://127.0.0.1:${localServerPort}/api/tags`, {
headers: {
Accept: 'application/json',
@ -208,7 +270,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
test.fail(`Delete ${model} model`, async () => {
test.skip(`Delete ${model} model`, async () => {
test.skip(isWindows, 'Model deletion is currently very buggy in azure cicd');
test.setTimeout(310_000);
const catalogPage = await aiLabPage.navigationBar.openCatalog();
@ -227,8 +289,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
let catalogPage: AILabCatalogPage;
test.beforeEach(`Open AI Lab Catalog`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
catalogPage = await aiLabPage.navigationBar.openCatalog();
@ -236,24 +297,24 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
});
test(`Download ${modelName} model`, async () => {
test.setTimeout(310_000);
test.setTimeout(610_000);
if (!(await catalogPage.isModelDownloaded(modelName))) {
await catalogPage.downloadModel(modelName);
}
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [5_000] })
.poll(async () => await waitForCatalogModel(modelName), { timeout: 600_000, intervals: [5_000] })
.toBeTruthy();
});
test(`Delete ${modelName} model`, async () => {
test.skip(isWindows, 'Model deletion is currently very buggy in azure cicd');
test.setTimeout(310_000);
test.setTimeout(610_000);
playExpect(await catalogPage.isModelDownloaded(modelName)).toBeTruthy();
await catalogPage.deleteModel(modelName);
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [2_500] })
.poll(async () => await waitForCatalogModel(modelName), { timeout: 600_000, intervals: [2_500] })
.toBeFalsy();
});
});
@ -270,8 +331,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
);
test.beforeAll(`Open AI Lab Catalog`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
catalogPage = await aiLabPage.navigationBar.openCatalog();
@ -299,6 +359,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
await playExpect(modelServiceDetailsPage.modelName).toContainText(modelName);
await playExpect(modelServiceDetailsPage.inferenceServerType).toContainText('Inference');
await playExpect(modelServiceDetailsPage.inferenceServerType).toContainText(/CPU|GPU/);
});
test(`Make GET request to the model service for ${modelName}`, async ({ request }) => {
@ -342,16 +403,36 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
}).toPass({ timeout: 600_000, intervals: [5_000] });
});
test(`Delete model service for ${modelName}`, async () => {
test(`Restart model service for ${modelName}`, async () => {
test.skip(modelName === 'ggerganov/whisper.cpp');
test.setTimeout(180_000);
await modelServiceDetailsPage.stopService();
await playExpect(modelServiceDetailsPage.startServiceButton).toBeEnabled({ timeout: 120_000 });
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await modelServiceDetailsPage.getServiceState(), { timeout: 120_000 })
.toBe('');
await modelServiceDetailsPage.startService();
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await modelServiceDetailsPage.getServiceState(), { timeout: 120_000 })
.toBe('RUNNING');
});
test(`Delete model service and model for ${modelName}`, async () => {
test.setTimeout(150_000);
const modelServicePage = await modelServiceDetailsPage.deleteService();
await playExpect(modelServicePage.heading).toBeVisible({ timeout: 120_000 });
await cleanupServices();
await deleteAllModels();
});
});
});
['lmstudio-community/granite-3.0-8b-instruct-GGUF'].forEach(modelName => {
test.describe.serial(`AI Lab playground creation and deletion`, () => {
// 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', 'TheBloke/Mistral-7B-Instruct-v0.2-GGUF'].forEach(modelName => {
test.describe.serial(`AI Lab playground creation and deletion for ${modelName}`, { tag: '@smoke' }, () => {
let catalogPage: AILabCatalogPage;
let playgroundsPage: AILabPlaygroundsPage;
let playgroundDetailsPage: AILabPlaygroundDetailsPage;
@ -361,8 +442,7 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
const systemPrompt = 'Always respond with: "Hello, I am Chat Bot"';
test.beforeAll(`Open AI Lab Catalog`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
catalogPage = await aiLabPage.navigationBar.openCatalog();
@ -370,13 +450,13 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
});
test(`Download ${modelName} model if not available`, async () => {
test.setTimeout(310_000);
test.setTimeout(610_000);
if (!(await catalogPage.isModelDownloaded(modelName))) {
await catalogPage.downloadModel(modelName);
}
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await waitForCatalogModel(modelName), { timeout: 300_000, intervals: [5_000] })
.poll(async () => await waitForCatalogModel(modelName), { timeout: 600_000, intervals: [5_000] })
.toBeTruthy();
});
@ -432,12 +512,13 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
test.afterAll(`Cleaning up service model`, async () => {
test.setTimeout(60_000);
await cleanupServiceModels();
await cleanupServices();
await deleteAllModels();
});
});
});
['Audio to Text', 'ChatBot', 'Summarizer', 'Code Generation', 'RAG Chatbot'].forEach(appName => {
AI_APPS.forEach(({ appName, appModel }) => {
test.describe.serial(`AI Recipe installation`, () => {
test.skip(
!process.env.EXT_TEST_RAG_CHATBOT && appName === 'RAG Chatbot',
@ -445,9 +526,8 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
);
let recipesCatalogPage: AILabRecipesCatalogPage;
test.beforeEach(`Open Recipes Catalog`, async ({ runner, page, navigationBar }) => {
[page, webview] = await handleWebview(runner, page, navigationBar);
aiLabPage = new AILabPage(page, webview);
test.beforeAll(`Open Recipes Catalog`, async ({ runner, page, navigationBar }) => {
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
recipesCatalogPage = await aiLabPage.navigationBar.openRecipesCatalog();
@ -455,23 +535,187 @@ test.describe.serial(`AI Lab extension installation and verification`, () => {
});
test(`Install ${appName} example app`, async () => {
test.skip(
appName === 'Object Detection' && isCI && !isMac,
'Currently we are facing issues with the Object Detection app installation on Windows and Linux CI.',
);
test.setTimeout(1_500_000);
const demoApp = await recipesCatalogPage.openRecipesCatalogApp(appName);
await demoApp.waitForLoad();
await demoApp.startNewDeployment();
});
test.afterEach(`Stop ${appName} app`, async ({ navigationBar }) => {
test(`Verify ${appName} app HTTP page is reachable`, async ({ request }) => {
test.setTimeout(60_000);
/// In the future, we could use this test for other AI applications
test.skip(
appName !== 'Object Detection' || (isCI && !isMac),
'Runs only for Object Detection app on macOS CI or any local platform',
);
const aiRunningAppsPage = await aiLabPage.navigationBar.openRunningApps();
const appPort = await aiRunningAppsPage.getAppPort(appName);
const response = await request.get(`http://localhost:${appPort}`, { timeout: 60_000 });
playExpect(response.ok()).toBeTruthy();
const body = await response.text();
playExpect(body).toContain('<title>Streamlit</title>');
});
test(`Verify that model service for the ${appName} is working`, async ({ request }) => {
test.skip(appName !== 'Function calling' && appName !== 'Audio to Text');
test.fail(
appName === 'Audio to Text',
'Expected failure due to issue #3111: https://github.com/containers/podman-desktop-extension-ai-lab/issues/3111',
);
test.setTimeout(600_000);
const modelServicePage = await aiLabPage.navigationBar.openServices();
const serviceDetailsPage = await modelServicePage.openServiceDetails(appModel);
await playExpect
// eslint-disable-next-line sonarjs/no-nested-functions
.poll(async () => await serviceDetailsPage.getServiceState(), { timeout: 60_000 })
.toBe('RUNNING');
const port = await serviceDetailsPage.getInferenceServerPort();
const baseUrl = `http://localhost:${port}`;
let response: APIResponse;
let expectedResponse: string;
switch (appModel) {
case 'ggerganov/whisper.cpp': {
expectedResponse =
'And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country';
const audioFileContent = fs.readFileSync(TEST_AUDIO_FILE_PATH);
response = await request.post(`${baseUrl}/inference`, {
headers: {
Accept: 'application/json',
},
multipart: {
file: {
name: 'test.wav',
mimeType: 'audio/wav',
buffer: audioFileContent,
},
},
timeout: 600_000,
});
break;
}
case 'ibm-granite/granite-3.3-8b-instruct-GGUF': {
expectedResponse = 'Prague';
response = await request.post(`${baseUrl}/v1/chat/completions`, {
data: {
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is the capital of Czech Republic?' },
],
},
timeout: 600_000,
});
break;
}
default:
throw new Error(`Unhandled model type: ${appModel}`);
}
playExpect(response.ok()).toBeTruthy();
const body = await response.body();
const text = body.toString();
playExpect(text).toContain(expectedResponse);
});
test(`${appName}: Restart, Stop, Delete. Clean up model service`, async () => {
test.skip(
appName === 'Object Detection' && isCI && !isMac,
'Currently we are facing issues with the Object Detection app installation on Windows and Linux CI.',
);
test.setTimeout(150_000);
await restartApp(appName);
await stopAndDeleteApp(appName);
await cleanupServiceModels();
await cleanupServices();
});
test.afterAll(`Ensure cleanup of "${appName}" app, related service, and images`, async ({ navigationBar }) => {
test.setTimeout(150_000);
await stopAndDeleteApp(appName);
await cleanupServices();
await deleteAllModels();
await deleteUnusedImages(navigationBar);
});
});
});
test.describe.serial('InstructLab container startup', { tag: '@instructlab' }, () => {
let instructLabPage: AILabTryInstructLabPage;
const instructLabContainerName = /^instructlab-\d+$/;
let exactInstructLabContainerName = '';
test.skip(!!process.env.GITHUB_ACTIONS && !!isLinux);
test.beforeAll('Open Try InstructLab page', async ({ runner, page, navigationBar }) => {
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
instructLabPage = await aiLabPage.navigationBar.openTryInstructLab();
await instructLabPage.waitForLoad();
});
test('Start and verify InstructLab container', async ({ page }) => {
test.setTimeout(1_000_000);
await playExpect(instructLabPage.startInstructLabButton).toBeVisible();
await playExpect(instructLabPage.startInstructLabButton).toBeEnabled();
await instructLabPage.startInstructLabButton.click();
await playExpect(instructLabPage.openInstructLabButton).toBeVisible({ timeout: 900_000 });
await playExpect(instructLabPage.openInstructLabButton).toBeEnabled({ timeout: 10_000 });
await playExpect(instructLabPage.statusMessageBox).toContainText('Starting InstructLab container');
const checkMarkLocator = instructLabPage.statusMessageBox.locator('[class*="text-green"]');
await playExpect(checkMarkLocator).toHaveCount(3);
await instructLabPage.openInstructLabButton.click();
const containerName = await page
.getByRole('region', { name: 'Header' })
.getByLabel(instructLabContainerName)
.textContent();
if (typeof containerName === 'string') {
exactInstructLabContainerName = containerName;
}
const containerDetailsPage = new ContainerDetailsPage(page, exactInstructLabContainerName);
await playExpect(containerDetailsPage.heading).toBeVisible();
await playExpect(containerDetailsPage.heading).toContainText(exactInstructLabContainerName);
await playExpect
.poll(async () => containerDetailsPage.getState(), { timeout: 90_000, intervals: [1_000] })
.toContain(ContainerState.Running);
});
test('Cleanup the InstructLab container', async ({ runner, page, navigationBar }) => {
const containerDetailsPage = new ContainerDetailsPage(page, exactInstructLabContainerName);
await playExpect(containerDetailsPage.heading).toBeVisible();
await containerDetailsPage.deleteContainer();
const containersPage = await navigationBar.openContainers();
await playExpect(containersPage.heading).toBeVisible({ timeout: 30_000 });
await playExpect
.poll(async () => containersPage.containerExists(exactInstructLabContainerName), { timeout: 100_000 })
.toBeFalsy();
aiLabPage = await reopenAILabDashboard(runner, page, navigationBar);
await aiLabPage.navigationBar.waitForLoad();
instructLabPage = await aiLabPage.navigationBar.openTryInstructLab();
await instructLabPage.waitForLoad();
await playExpect(instructLabPage.startInstructLabButton).toBeEnabled();
});
});
});
async function cleanupServiceModels(): Promise<void> {
async function cleanupServices(): Promise<void> {
try {
const modelServicePage = await aiLabPage.navigationBar.openServices();
await modelServicePage.waitForLoad();
@ -482,9 +726,36 @@ async function cleanupServiceModels(): 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> {
const aiRunningAppsPage = await aiLabPage.navigationBar.openRunningApps();
const aiApp = await aiRunningAppsPage.getRowForApp(appName);
await aiRunningAppsPage.waitForLoad();
await playExpect.poll(async () => await aiRunningAppsPage.appExists(appName), { timeout: 10_000 }).toBeTruthy();
await playExpect
.poll(async () => await aiRunningAppsPage.getCurrentStatusForApp(appName), { timeout: 60_000 })
.toBe('RUNNING');
await aiRunningAppsPage.restartApp(appName);
const appProgressBar = aiApp.getByRole('progressbar', { name: 'Loading' });
await playExpect(appProgressBar).toBeVisible({ timeout: 60_000 });
await playExpect
.poll(async () => await aiRunningAppsPage.getCurrentStatusForApp(appName), { timeout: 60_000 })
.toBe('RUNNING');
}
async function stopAndDeleteApp(appName: string): Promise<void> {
const aiRunningAppsPage = await aiLabPage.navigationBar.openRunningApps();
await aiRunningAppsPage.waitForLoad();
if (!(await aiRunningAppsPage.appExists(appName))) {
console.log(`"${appName}" is not present in the running apps list. Skipping stop and delete operations.`);
return;
}
await playExpect.poll(async () => await aiRunningAppsPage.appExists(appName), { timeout: 10_000 }).toBeTruthy();
await playExpect
.poll(async () => await aiRunningAppsPage.getCurrentStatusForApp(appName), { timeout: 60_000 })

View File

@ -17,17 +17,30 @@
***********************************************************************/
import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
export abstract class AILabBasePage {
readonly page: Page;
readonly webview: Page;
readonly heading: Locator;
readonly gpuSupportBanner: Locator;
readonly enableGpuButton: Locator;
readonly dontDisplayButton: Locator;
constructor(page: Page, webview: Page, heading: string | undefined) {
this.page = page;
this.webview = webview;
this.heading = webview.getByRole('heading', { name: heading, exact: true }).first();
this.gpuSupportBanner = this.webview.getByLabel('GPU promotion banner');
this.enableGpuButton = this.gpuSupportBanner.getByRole('button', { name: 'Enable GPU support' });
this.dontDisplayButton = this.gpuSupportBanner.getByRole('button', { name: `Don't display anymore` });
}
abstract waitForLoad(): Promise<void>;
async enableGpuSupport(): Promise<void> {
await playExpect(this.gpuSupportBanner).toBeVisible();
await this.enableGpuButton.click();
await playExpect(this.gpuSupportBanner).not.toBeVisible();
}
}

View File

@ -19,7 +19,7 @@
import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
import { AILabCreatingModelServicePage } from './ai-lab-creating-model-service-page';
export class AILabCatalogPage extends AILabBasePage {
@ -50,6 +50,12 @@ export class AILabCatalogPage extends AILabBasePage {
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> {
const modelRow = await this.getModelRowByName(modelName);
if (!modelRow) {
@ -75,16 +81,35 @@ export class AILabCatalogPage extends AILabBasePage {
}
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);
if (!modelRow) {
throw new Error(`Model ${modelName} not found`);
}
const deleteButton = modelRow.getByRole('button', { name: 'Delete Model' });
await playExpect(deleteButton).toBeEnabled();
await playExpect.poll(async () => await deleteButton.isEnabled(), { timeout: 10_000 }).toBeTruthy();
await deleteButton.focus();
await deleteButton.click();
await this.page.waitForTimeout(1_000);
await handleConfirmationDialog(this.page, 'Podman AI Lab', 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> {

View File

@ -21,7 +21,7 @@ import { expect as playExpect } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { AILabNavigationBar } from './ai-lab-navigation-bar';
export class AILabPage extends AILabBasePage {
export class AILabDashboardPage extends AILabBasePage {
readonly navigationBar: AILabNavigationBar;
constructor(page: Page, webview: Page) {

View File

@ -19,8 +19,9 @@
import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
import { AILabCreatingModelServicePage } from './ai-lab-creating-model-service-page';
import { AILabServiceDetailsPage } from './ai-lab-service-details-page';
export class AiModelServicePage extends AILabBasePage {
readonly additionalActions: Locator;
@ -59,13 +60,35 @@ export class AiModelServicePage extends AILabBasePage {
await playExpect(this.deleteSelectedItems).toBeEnabled();
await this.deleteSelectedItems.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
}
async getCurrentModelCount(): Promise<number> {
return (await this.getAllTableRows()).length;
}
async openServiceDetails(modelName: string): Promise<AILabServiceDetailsPage> {
const serviceRow = await this.getServiceByModel(modelName);
if (serviceRow === undefined) {
throw new Error(`Model [${modelName}] service doesn't exist`);
}
const serviceRowName = serviceRow.getByRole('cell').nth(3);
await serviceRowName.click();
return new AILabServiceDetailsPage(this.page, this.webview);
}
async getServiceByModel(modelName: string): Promise<Locator | undefined> {
const rows = await this.getAllTableRows();
for (let rowNum = 1; rowNum < rows.length; rowNum++) {
//skip header
const serviceModel = rows[rowNum].getByRole('cell').nth(4);
if ((await serviceModel.textContent()) === modelName) {
return rows[rowNum];
}
}
return undefined;
}
private async getAllTableRows(): Promise<Locator[]> {
return await this.webview.getByRole('row').all();
}

View File

@ -25,9 +25,12 @@ import { AiModelServicePage } from './ai-lab-model-service-page';
import { AILabCatalogPage } from './ai-lab-catalog-page';
import { AILabPlaygroundsPage } from './ai-lab-playgrounds-page';
import { AILabLocalServerPage } from './ai-lab-local-server-page';
import { AILabDashboardPage } from './ai-lab-dashboard-page';
import { AILabTryInstructLabPage } from './ai-lab-try-instructlab-page';
export class AILabNavigationBar extends AILabBasePage {
readonly navigationBar: Locator;
readonly dashboardButton: Locator;
readonly recipesCatalogButton: Locator;
readonly runningAppsButton: Locator;
readonly catalogButton: Locator;
@ -35,10 +38,12 @@ export class AILabNavigationBar extends AILabBasePage {
readonly playgroundsButton: Locator;
readonly tuneButton: Locator;
readonly localServerButton: Locator;
readonly tryInstructLabButton: Locator;
constructor(page: Page, webview: Page) {
super(page, webview, undefined);
this.navigationBar = this.webview.getByRole('navigation', { name: 'PreferencesNavigation' });
this.dashboardButton = this.navigationBar.getByRole('link', { name: 'Dashboard', exact: true });
this.recipesCatalogButton = this.navigationBar.getByRole('link', { name: 'Recipe Catalog', exact: true });
this.runningAppsButton = this.navigationBar.getByRole('link', { name: 'Running' });
this.catalogButton = this.navigationBar.getByRole('link', { name: 'Catalog', exact: true });
@ -46,12 +51,19 @@ export class AILabNavigationBar extends AILabBasePage {
this.playgroundsButton = this.navigationBar.getByRole('link', { name: 'Playgrounds' });
this.tuneButton = this.navigationBar.getByRole('link', { name: 'Tune with InstructLab' });
this.localServerButton = this.navigationBar.getByRole('link', { name: 'Local Server' });
this.tryInstructLabButton = this.navigationBar.getByRole('link', { name: 'Try InstructLab' });
}
async waitForLoad(): Promise<void> {
await playExpect(this.navigationBar).toBeVisible();
}
async openDashboard(): Promise<AILabDashboardPage> {
await playExpect(this.dashboardButton).toBeEnabled();
await this.dashboardButton.click();
return new AILabDashboardPage(this.page, this.webview);
}
async openRecipesCatalog(): Promise<AILabRecipesCatalogPage> {
await playExpect(this.recipesCatalogButton).toBeEnabled();
await this.recipesCatalogButton.click();
@ -87,4 +99,10 @@ export class AILabNavigationBar extends AILabBasePage {
await this.localServerButton.click();
return new AILabLocalServerPage(this.page, this.webview);
}
async openTryInstructLab(): Promise<AILabTryInstructLabPage> {
await playExpect(this.tryInstructLabButton).toBeEnabled();
await this.tryInstructLabButton.click();
return new AILabTryInstructLabPage(this.page, this.webview);
}
}

View File

@ -20,7 +20,7 @@ import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { AILabPlaygroundsPage } from './ai-lab-playgrounds-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
export class AILabPlaygroundDetailsPage extends AILabBasePage {
readonly name: string;
@ -73,14 +73,14 @@ export class AILabPlaygroundDetailsPage extends AILabBasePage {
async deletePlayground(): Promise<AILabPlaygroundsPage> {
await playExpect(this.deletePlaygroundButton).toBeEnabled();
await this.deletePlaygroundButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
return new AILabPlaygroundsPage(this.page, this.webview);
}
async submitUserInput(prompt: string): Promise<void> {
await this.promptTextAreaLocator.fill(prompt);
await playExpect(this.promptTextAreaLocator).toHaveValue(prompt);
await playExpect(this.sendPromptButton).toBeEnabled({ timeout: 30_000 });
await playExpect(this.sendPromptButton).toBeEnabled({ timeout: 80_000 });
await this.sendPromptButton.click();
}

View File

@ -19,7 +19,7 @@
import type { Locator, Page } from '@playwright/test';
import { expect as playExpect } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
import { AILabPlaygroundDetailsPage } from './ai-lab-playground-details-page';
export class AILabPlaygroundsPage extends AILabBasePage {
@ -60,7 +60,7 @@ export class AILabPlaygroundsPage extends AILabBasePage {
const deleteButton = playgroundRow.getByRole('button', { name: 'Delete conversation', exact: true });
await playExpect(deleteButton).toBeEnabled();
await deleteButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
return this;
}

View File

@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (C) 2024 Red Hat, Inc.
* Copyright (C) 2024-2025 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,7 @@
import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
export class AiRunningAppsPage extends AILabBasePage {
constructor(page: Page, webview: Page) {
@ -46,6 +46,15 @@ export class AiRunningAppsPage extends AILabBasePage {
return `${await row.getByRole('cell').nth(1).getByRole('status').getAttribute('title', { timeout: 60_000 })}`;
}
async restartApp(appName: string): Promise<void> {
const dropDownMenu = await this.openKebabMenuForApp(appName);
const restartButton = dropDownMenu.getByTitle('Restart AI App');
await playExpect(restartButton).toBeVisible();
await restartButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
}
async stopApp(appName: string): Promise<void> {
const row = await this.getRowForApp(appName);
const stopButton = row.getByLabel('Stop AI App');
@ -53,20 +62,21 @@ export class AiRunningAppsPage extends AILabBasePage {
await stopButton.click();
}
async openKebabMenuForApp(appName: string): Promise<void> {
async openKebabMenuForApp(appName: string): Promise<Locator> {
const row = await this.getRowForApp(appName);
const kebabMenu = row.getByLabel('kebab menu');
await playExpect(kebabMenu).toBeEnabled();
await kebabMenu.click();
return this.webview.getByTitle('Drop Down Menu Items');
}
async deleteAIApp(appName: string): Promise<void> {
await this.openKebabMenuForApp(appName);
const deleteButton = this.webview.getByRole('none').nth(2);
const dropDownMenu = await this.openKebabMenuForApp(appName);
const deleteButton = dropDownMenu.getByTitle('Delete AI App');
await playExpect(deleteButton).toBeVisible();
await deleteButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
}
async appExists(appName: string): Promise<boolean> {
@ -82,6 +92,18 @@ export class AiRunningAppsPage extends AILabBasePage {
}
}
async getAppPort(appName: string): Promise<string> {
const appRow = await this.getRowForApp(appName);
//Update this locator after issue https://github.com/containers/podman-desktop-extension-ai-lab/issues/3113 is resolved
const portCell = appRow.getByRole('cell').nth(3);
const rawPortText = await portCell.getByText(/PORT\s\d+/).textContent();
if (!rawPortText) {
throw new Error(`Failed to extract port for app: ${appName}.`);
}
const portNumber = rawPortText.replace(/[^\d]/g, '');
return portNumber;
}
private async getAllTableRows(): Promise<Locator[]> {
return await this.webview.getByRole('row').all();
}

View File

@ -20,7 +20,7 @@ import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { AiModelServicePage } from './ai-lab-model-service-page';
import { handleConfirmationDialog } from '@podman-desktop/tests-playwright';
import { handleConfirmationDialog, podmanAILabExtension } from '@podman-desktop/tests-playwright';
export class AILabServiceDetailsPage extends AILabBasePage {
readonly endpointURL: Locator;
@ -29,6 +29,7 @@ export class AILabServiceDetailsPage extends AILabBasePage {
readonly codeSnippet: Locator;
readonly deleteServiceButton: Locator;
readonly stopServiceButton: Locator;
readonly startServiceButton: Locator;
constructor(page: Page, webview: Page) {
super(page, webview, 'Service details');
@ -38,6 +39,7 @@ export class AILabServiceDetailsPage extends AILabBasePage {
this.codeSnippet = this.webview.getByLabel('Code Snippet', { exact: true });
this.deleteServiceButton = this.webview.getByRole('button', { name: 'Delete service' });
this.stopServiceButton = this.webview.getByRole('button', { name: 'Stop service' });
this.startServiceButton = this.webview.getByRole('button', { name: 'Start service' });
}
async waitForLoad(): Promise<void> {
@ -47,13 +49,28 @@ export class AILabServiceDetailsPage extends AILabBasePage {
async deleteService(): Promise<AiModelServicePage> {
await playExpect(this.deleteServiceButton).toBeEnabled();
await this.deleteServiceButton.click();
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Confirm');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Confirm');
return new AiModelServicePage(this.page, this.webview);
}
async stopService(): Promise<void> {
await playExpect(this.stopServiceButton).toBeEnabled();
await this.stopServiceButton.click();
}
async startService(): Promise<void> {
await playExpect(this.startServiceButton).toBeEnabled();
await this.startServiceButton.click();
}
async getInferenceServerPort(): Promise<string> {
const split = (await this.endpointURL.textContent())?.split(':');
const port = split ? split[split.length - 1].split('/')[0] : '';
return port;
}
async getServiceState(): Promise<string> {
const serviceState = await this.webview.getByRole('status').getAttribute('title');
return serviceState ?? 'UNKNOWN';
}
}

View File

@ -19,7 +19,7 @@
import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
import { StatusBar, handleConfirmationDialog, waitUntil } from '@podman-desktop/tests-playwright';
import { StatusBar, handleConfirmationDialog, podmanAILabExtension, waitUntil } from '@podman-desktop/tests-playwright';
import { AILabNavigationBar } from './ai-lab-navigation-bar';
export class AILabStartRecipePage extends AILabBasePage {
@ -33,7 +33,7 @@ export class AILabStartRecipePage extends AILabBasePage {
super(page, webview, 'Start recipe');
this.recipeStatus = this.webview.getByRole('status');
this.applicationDetailsPanel = this.webview.getByLabel('application details panel');
this.startRecipeButton = this.webview.getByRole('button', { name: /Start(\s+([a-z]+\s+)+)recipe/i });
this.startRecipeButton = this.webview.getByRole('button', { name: /^Start .+ recipe$/i });
this.openAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Open AI App' });
this.deleteAIAppButton = this.applicationDetailsPanel.getByRole('button', { name: 'Delete AI App' });
}
@ -46,7 +46,7 @@ export class AILabStartRecipePage extends AILabBasePage {
await playExpect(this.startRecipeButton).toBeEnabled();
await this.startRecipeButton.click();
try {
await handleConfirmationDialog(this.page, 'Podman AI Lab', true, 'Reset');
await handleConfirmationDialog(this.page, podmanAILabExtension.extensionName, true, 'Reset');
} catch (error) {
console.warn(`Warning: Could not reset the app, repository probably clean.\n\t${error}`);
}

View File

@ -0,0 +1,38 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* 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
*025
* 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.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import { expect as playExpect } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AILabBasePage } from './ai-lab-base-page';
export class AILabTryInstructLabPage extends AILabBasePage {
readonly startInstructLabButton: Locator;
readonly openInstructLabButton: Locator;
readonly statusMessageBox: Locator;
constructor(page: Page, webview: Page) {
super(page, webview, 'Run InstructLab as a container');
this.startInstructLabButton = this.webview.getByRole('button', { name: 'Start InstructLab container' });
this.openInstructLabButton = this.webview.getByRole('button', { name: 'Open InstructLab container' });
this.statusMessageBox = this.webview.getByRole('status');
}
async waitForLoad(): Promise<void> {
await playExpect(this.heading).toBeVisible();
}
}

View File

@ -16,11 +16,32 @@
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { Page } from '@playwright/test';
import { ExtensionDetailsPage } from '@podman-desktop/tests-playwright';
import type { Locator, Page } from '@playwright/test';
import { expect as playExpect, ExtensionDetailsPage } from '@podman-desktop/tests-playwright';
export class AILabExtensionDetailsPage extends ExtensionDetailsPage {
readonly errorTab: Locator;
constructor(page: Page) {
super(page, 'Podman AI Lab extension');
this.errorTab = this.tabs.getByRole('button', { name: 'Error' });
}
async waitForLoad(): Promise<void> {
await playExpect(this.heading).toBeVisible();
}
async checkIsActive(statusTest: string): Promise<void> {
await playExpect(this.status).toHaveText(statusTest);
}
async checkForErrors(): Promise<void> {
// we would like to propagate the error's stack trace into test failure message
let stackTrace = '';
if ((await this.errorTab.count()) > 0) {
await this.activateTab('Error');
stackTrace = await this.errorStackTrace.innerText();
}
await playExpect(this.errorTab, `Error Tab was present with stackTrace: ${stackTrace}`).not.toBeVisible();
}
}

View File

@ -0,0 +1,51 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { Locator, Page } from '@playwright/test';
import { expect as playExpect, PreferencesPage } from '@podman-desktop/tests-playwright';
export class ExtensionAILabPreferencesPage extends PreferencesPage {
public static readonly tabName = 'Extension: AI Lab';
readonly heading: Locator;
readonly experimentalGPUCheckbox: Locator;
constructor(page: Page) {
super(page);
this.heading = this.content.getByText(ExtensionAILabPreferencesPage.tabName, { exact: true });
this.experimentalGPUCheckbox = this.content.getByRole('checkbox', {
name: 'Experimental GPU support for inference servers',
});
}
async waitForLoad(): Promise<void> {
await playExpect(this.heading).toBeVisible();
}
public async disableGPUPreference(): Promise<void> {
await this.experimentalGPUCheckbox.uncheck({ force: true });
await playExpect(this.experimentalGPUCheckbox).not.toBeChecked();
}
public async enableGPUPreference(): Promise<void> {
await this.experimentalGPUCheckbox.check({ force: true });
await playExpect(this.experimentalGPUCheckbox).toBeChecked();
}
public async isGPUPreferenceEnabled(): Promise<boolean> {
return await this.experimentalGPUCheckbox.isChecked();
}
}

View File

@ -0,0 +1,91 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { Page } from '@playwright/test';
import type { Runner, NavigationBar, ExtensionCardPage } from '@podman-desktop/tests-playwright';
import { expect as playExpect, podmanAILabExtension } from '@podman-desktop/tests-playwright';
import type { AILabDashboardPage } from 'src/model/ai-lab-dashboard-page';
import { handleWebview } from './webviewHandler';
import { ExtensionAILabPreferencesPage } from 'src/model/preferences-extension-ai-lab-page';
import { AILabExtensionDetailsPage } from 'src/model/podman-extension-ai-lab-details-page';
export async function reopenAILabDashboard(
runner: Runner,
page: Page,
navigationBar: NavigationBar,
): Promise<AILabDashboardPage> {
const dashboardPage = await navigationBar.openDashboard();
await playExpect(dashboardPage.mainPage).toBeVisible();
// eslint-disable-next-line @typescript-eslint/no-unused-vars, sonarjs/no-unused-vars
const [_locPage, _webview, aiLabNavigationBar] = await handleWebview(runner, page, navigationBar);
const aiLabDashboardPage = await aiLabNavigationBar.openDashboard();
await aiLabDashboardPage.waitForLoad();
return aiLabDashboardPage;
}
export async function openAILabPreferences(
navigationBar: NavigationBar,
page: Page,
): Promise<ExtensionAILabPreferencesPage> {
const dashboardPage = await navigationBar.openDashboard();
await playExpect(dashboardPage.mainPage).toBeVisible();
const settingsBar = await navigationBar.openSettings();
await playExpect(settingsBar.preferencesTab).toBeVisible();
await settingsBar.expandPreferencesTab();
await playExpect(settingsBar.preferencesTab).toBeVisible();
await settingsBar.getPreferencesLinkLocator(ExtensionAILabPreferencesPage.tabName).click();
const aiLabPreferencesPage = new ExtensionAILabPreferencesPage(page);
await aiLabPreferencesPage.waitForLoad();
return aiLabPreferencesPage;
}
export async function openAILabExtensionDetails(navigationBar: NavigationBar): Promise<AILabExtensionDetailsPage> {
const extensionCard = await getExtensionCard(navigationBar);
const extensionDetails = await extensionCard.openExtensionDetails(podmanAILabExtension.extensionFullName);
const aiLabExtensionDetails = new AILabExtensionDetailsPage(extensionDetails.page);
await aiLabExtensionDetails.waitForLoad();
return aiLabExtensionDetails;
}
export async function getExtensionCard(navigationBar: NavigationBar): Promise<ExtensionCardPage> {
const extensions = await navigationBar.openExtensions();
const extensionCard = await extensions.getInstalledExtension(
podmanAILabExtension.extensionLabel,
podmanAILabExtension.extensionFullLabel,
);
return extensionCard;
}
export async function waitForExtensionToInitialize(navigationBar: NavigationBar): Promise<void> {
const extensions = await navigationBar.openExtensions();
await playExpect
.poll(async () => await extensions.extensionIsInstalled(podmanAILabExtension.extensionFullLabel), {
timeout: 30000,
})
.toBeTruthy();
}
export async function getExtensionVersion(navigationBar: NavigationBar): Promise<string> {
const extensionsPage = await navigationBar.openExtensions();
const extensionVersion = await extensionsPage.getInstalledExtensionVersion(
podmanAILabExtension.extensionLabel,
podmanAILabExtension.extensionFullLabel,
);
playExpect(extensionVersion, `Extension version could not be retrieved.`).toBeDefined();
return String(extensionVersion);
}

View File

@ -19,8 +19,13 @@
import type { Page } from '@playwright/test';
import type { NavigationBar, Runner } from '@podman-desktop/tests-playwright';
import { expect as playExpect } from '@podman-desktop/tests-playwright';
import { AILabNavigationBar } from 'src/model/ai-lab-navigation-bar';
export async function handleWebview(runner: Runner, page: Page, navigationBar: NavigationBar): Promise<[Page, Page]> {
export async function handleWebview(
runner: Runner,
page: Page,
navigationBar: NavigationBar,
): Promise<[Page, Page, AILabNavigationBar]> {
const AI_LAB_NAVBAR_EXTENSION_LABEL: string = 'AI Lab';
const AI_LAB_PAGE_BODY_LABEL: string = 'Webview AI Lab';
@ -43,6 +48,6 @@ export async function handleWebview(runner: Runner, page: Page, navigationBar: N
console.log(`element is null`);
}
});
return [mainPage, webViewPage];
const aiLabNavigationBar = new AILabNavigationBar(mainPage, webViewPage);
return [mainPage, webViewPage, aiLabNavigationBar];
}