diff --git a/docs-site/content/cookbook/pan2/pan2.mdx b/docs-site/content/cookbook/pan2/pan2.mdx new file mode 100644 index 000000000..07836529a --- /dev/null +++ b/docs-site/content/cookbook/pan2/pan2.mdx @@ -0,0 +1,7 @@ +--- +title: PAN 2 +description: Configure text-to-video and first-frame image-to-video generation with PAN 2. +mode: frame +--- + + diff --git a/docs-site/lib/cookbook-pages.ts b/docs-site/lib/cookbook-pages.ts index c6d5d02bb..c2b2fe1a1 100644 --- a/docs-site/lib/cookbook-pages.ts +++ b/docs-site/lib/cookbook-pages.ts @@ -66,6 +66,15 @@ export const recipePages = [ description: "Turn a reference image into video with Wan2.1.", tags: ["Image to video", "14B", "480p"], }, + { + id: "pan2", + family: "PAN 2", + title: "PAN 2", + mark: "PAN", + path: "/cookbook/pan2/pan2", + description: "Text-to-video and first-frame image-to-video from the 29B PAN 2 model.", + tags: ["Text or image to video", "29B"], + }, { id: "ltx2-distilled", family: "LTX", @@ -128,6 +137,7 @@ const backendModels: Record = { "FastVideo/FastWan2.1-T2V-1.3B-Diffusers": "fastwan21", "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers": "wan21-i2v", "FastVideo/LTX2-Distilled-Diffusers": "ltx2-distilled", + "IFM/PAN2": "pan2", }; const TASK_GROUPS: Array<[RegExp, string]> = [ [/world/, "Text to world"], diff --git a/docs-site/navigation.json b/docs-site/navigation.json index 3b328cf38..e70b0fa5c 100644 --- a/docs-site/navigation.json +++ b/docs-site/navigation.json @@ -259,7 +259,11 @@ "collapsed": true, "items": [ { - "label": "Recipes and setup", + "label": "PAN 2", + "href": "/cookbook/pan2/pan2" + }, + { + "label": "Family guide", "href": "/cookbook/pan2" }, { diff --git a/docs-site/tests/cookbook.spec.ts b/docs-site/tests/cookbook.spec.ts index 644c17925..c7d7a95d6 100644 --- a/docs-site/tests/cookbook.spec.ts +++ b/docs-site/tests/cookbook.spec.ts @@ -443,19 +443,24 @@ test("source-backed serving pages expose their actual server settings", async ({ await expect(page.getByRole("button", { name: "Copy config + command", exact: true })).toBeEnabled(); }); -test("PAN 2 has a family card with the IFM logo and a source-backed serving page", async ({ page }) => { - await page.goto("/FastVideo/cookbook/pan2"); - const pan2 = page.locator(".recipe-card").filter({ hasText: "PAN 2" }); +test("PAN 2 has a family card with the IFM logo and an editable serving deployment", async ({ page }) => { + await page.goto("/FastVideo/cookbook"); + await page.getByLabel("Search recipes").fill("PAN 2"); + const pan2 = page.locator(".recipe-card"); + await expect(pan2).toHaveCount(1); await expect(pan2.locator("img")).toHaveAttribute("src", /\/ifm\.webp$/); await expect(pan2.locator(".recipe-card-kicker")).toHaveText("IFM"); await pan2.click(); + await expect(page).toHaveURL(/cookbook\/pan2\/pan2$/); + await expect(page.locator(".recipe-matrix")).toBeVisible({ timeout: 30000 }); await expect(page.getByRole("heading", { level: 1 })).toHaveText("PAN 2"); - await chip(page, "Run mode", "Serving"); - await page.getByRole("tab", { name: "config.yaml", exact: true }).click(); - const config = page.getByLabel("Source example", { exact: true }); - await expect(config).toContainText("model_path: IFM/PAN2"); - await expect(config).toContainText("served_model_name: pan2"); - await expect(page.getByRole("button", { name: "Copy config + command", exact: true })).toBeEnabled(); + await settings(page, "Serving"); + await page.getByLabel("Port", { exact: true }).fill("9010"); + await output(page, "config.yaml"); + await expect(page.getByLabel("Generated YAML", { exact: true })).toContainText(/"port": 9010/); + await expect(page.getByLabel("Generated YAML", { exact: true })).toContainText(/"model_path": "IFM\/PAN2"/); + await expect(page.getByLabel("Generated YAML", { exact: true })).toContainText(/"dit_layerwise": false/); + await expect(page.getByRole("button", { name: "Download YAML", exact: false })).toBeEnabled(); }); test("source model hardware preferences survive hydration and reload", async ({ page }) => {