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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/sdk/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@
- `lucy-2.5` - Real-time video editing (supports reference image)
- `lucy-vton-2` - Real-time virtual try-on 2
- `lucy-vton-3` - Real-time virtual try-on 3
- `lucy-vton-3.5` - Real-time virtual try-on 3.5
- `lucy-restyle-2` - Real-time video restyling
- `lucy-latest`, `lucy-vton-latest`, `lucy-restyle-latest` - Server-resolved aliases for the latest stable version (`lucy-vton-latest` → `lucy-vton-3`)
- `lucy-latest`, `lucy-vton-latest`, `lucy-restyle-latest` - Server-resolved aliases for the latest stable version (`lucy-vton-latest` → `lucy-vton-3.5`)
- Deprecated: `lucy-2.1-vton-2` → `lucy-vton-2`

### Video Models (Queue API)
Expand All @@ -82,8 +83,9 @@
- `lucy-2.5` - long-form video editing (720p)
- `lucy-vton-2` - virtual try-on 2 video editing
- `lucy-vton-3` - virtual try-on 3 video editing
- `lucy-vton-3.5` - virtual try-on 3.5 video editing
- `lucy-restyle-2` - video restyling
- `lucy-latest`, `lucy-vton-latest`, `lucy-restyle-latest`, `lucy-clip-latest` - Server-resolved aliases (`lucy-vton-latest` → `lucy-vton-3`)
- `lucy-latest`, `lucy-vton-latest`, `lucy-restyle-latest`, `lucy-clip-latest` - Server-resolved aliases (`lucy-vton-latest` → `lucy-vton-3.5`)
- Deprecated: `lucy-2.1-vton-2` → `lucy-vton-2`

### Image Models (Process API)
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ <h3>Configuration</h3>
<option value="lucy-2.5">Lucy 2.5</option>
<option value="lucy-vton-2">Lucy VTON 2</option>
<option value="lucy-vton-3">Lucy VTON 3</option>
<option value="lucy-vton-3.5">Lucy VTON 3.5</option>
<option value="lucy-restyle-2">Lucy Restyle 2</option>
</optgroup>
<optgroup label="Deprecated aliases">
Expand Down
9 changes: 8 additions & 1 deletion packages/sdk/src/process/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,14 @@ export type ModelSpecificInputs<T extends ModelDefinition> = T["name"] extends "
? ImageEditingInputs
: T["name"] extends "lucy-restyle-v2v" | "lucy-restyle-2"
? VideoRestyleInputs
: T["name"] extends "lucy-2.1" | "lucy-2.5" | "lucy-vton-2" | "lucy-vton-3" | "lucy-2.1-vton-2" | "lucy-vton-latest"
: T["name"] extends
| "lucy-2.1"
| "lucy-2.5"
| "lucy-vton-2"
| "lucy-vton-3"
| "lucy-vton-3.5"
| "lucy-2.1-vton-2"
| "lucy-vton-latest"
? VideoEdit2Inputs
: T["name"] extends "lucy-pro-v2v" | "lucy-clip"
? VideoEditInputs
Expand Down
37 changes: 31 additions & 6 deletions packages/sdk/src/shared/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const CANONICAL_MODEL_NAMES = [
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
"lucy-restyle-2",
"lucy-clip",
"lucy-image-2",
Expand All @@ -17,6 +18,7 @@ const CANONICAL_REALTIME_MODEL_NAMES = [
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
"lucy-restyle-2",
] as const;
const CANONICAL_VIDEO_MODEL_NAMES = [
Expand All @@ -25,6 +27,7 @@ const CANONICAL_VIDEO_MODEL_NAMES = [
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
"lucy-restyle-2",
] as const;
const CANONICAL_IMAGE_MODEL_NAMES = ["lucy-image-2"] as const;
Expand Down Expand Up @@ -69,6 +72,7 @@ export const realtimeModels = z.union([
z.literal("lucy-2.5"),
z.literal("lucy-vton-2"),
z.literal("lucy-vton-3"),
z.literal("lucy-vton-3.5"),
z.literal("lucy-restyle-2"),
// Latest aliases (server-side resolution)
z.literal("lucy-latest"),
Expand All @@ -84,6 +88,7 @@ export const videoModels = z.union([
z.literal("lucy-2.5"),
z.literal("lucy-vton-2"),
z.literal("lucy-vton-3"),
z.literal("lucy-vton-3.5"),
z.literal("lucy-restyle-2"),
// Latest aliases (server-side resolution)
z.literal("lucy-latest"),
Expand Down Expand Up @@ -261,6 +266,7 @@ export const modelInputSchemas = {
"lucy-2.5": videoEdit2Schema,
"lucy-vton-2": videoEdit2Schema,
"lucy-vton-3": videoEdit2Schema,
"lucy-vton-3.5": videoEdit2Schema,
// Latest aliases (server-side resolution)
"lucy-latest": videoEdit2Schema,
"lucy-vton-latest": videoEdit2Schema,
Expand Down Expand Up @@ -369,6 +375,14 @@ const _models = {
height: 624,
inputSchema: z.object({}),
},
"lucy-vton-3.5": {
urlPath: "/v1/stream",
name: "lucy-vton-3.5" as const,
fps: { ideal: 30, max: 30 },
width: 1280,
height: 720,
inputSchema: z.object({}),
},
"lucy-restyle-2": {
urlPath: "/v1/stream",
name: "lucy-restyle-2" as const,
Expand All @@ -386,13 +400,13 @@ const _models = {
height: 624,
inputSchema: z.object({}),
},
// Server-side alias currently resolves to lucy-vton-3.
// Server-side alias currently resolves to lucy-vton-3.5.
"lucy-vton-latest": {
urlPath: "/v1/stream",
name: "lucy-vton-latest" as const,
fps: { ideal: 30, max: 30 },
width: 1088,
height: 624,
width: 1280,
height: 720,
inputSchema: z.object({}),
},
"lucy-restyle-latest": {
Expand Down Expand Up @@ -492,6 +506,15 @@ const _models = {
height: 624,
inputSchema: modelInputSchemas["lucy-vton-3"],
},
"lucy-vton-3.5": {
urlPath: "/v1/generate/lucy-vton-3.5",
queueUrlPath: "/v1/jobs/lucy-vton-3.5",
name: "lucy-vton-3.5" as const,
fps: 20,
width: 1280,
height: 720,
inputSchema: modelInputSchemas["lucy-vton-3.5"],
},
"lucy-restyle-2": {
urlPath: "/v1/generate/lucy-restyle-2",
queueUrlPath: "/v1/jobs/lucy-restyle-2",
Expand All @@ -511,14 +534,14 @@ const _models = {
height: 624,
inputSchema: modelInputSchemas["lucy-latest"],
},
// Server-side alias currently resolves to lucy-vton-3.
// Server-side alias currently resolves to lucy-vton-3.5.
"lucy-vton-latest": {
urlPath: "/v1/generate/lucy-vton-latest",
queueUrlPath: "/v1/jobs/lucy-vton-latest",
name: "lucy-vton-latest" as const,
fps: 20,
width: 1088,
height: 624,
width: 1280,
height: 720,
inputSchema: modelInputSchemas["lucy-vton-latest"],
},
"lucy-restyle-latest": {
Expand Down Expand Up @@ -607,6 +630,7 @@ export const models = {
* - `"lucy-2.5"` - Lucy 2.5 realtime video editing
* - `"lucy-vton-2"` - Lucy virtual try-on 2
* - `"lucy-vton-3"` - Lucy virtual try-on 3
* - `"lucy-vton-3.5"` - Lucy virtual try-on 3.5 (latest)
* - `"lucy-restyle-2"` - Realtime video restyling
*/
realtime: <T extends RealTimeModels>(model: T): ModelDefinition<T> => {
Expand All @@ -626,6 +650,7 @@ export const models = {
* - `"lucy-2.5"` - Long-form video editing (Lucy 2.5)
* - `"lucy-vton-2"` - Virtual try-on 2 video editing
* - `"lucy-vton-3"` - Virtual try-on 3 video editing
* - `"lucy-vton-3.5"` - Virtual try-on 3.5 video editing (latest)
* - `"lucy-restyle-2"` - Video restyling
*/
video: <T extends VideoModels>(model: T): ModelDefinition<T> & { fps: number; queueUrlPath: string } => {
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/tests/e2e-realtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const REALTIME_MODELS: RealTimeModels[] = [
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
];

const TIMEOUT = 1 * 60 * 1000; // 1 minute
Expand Down
23 changes: 23 additions & 0 deletions packages/sdk/tests/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,29 @@ describe.concurrent("E2E Tests", { timeout: TIMEOUT, retry: 2 }, () => {
await expectResult(result, "lucy-vton-3-reference_image", ".mp4");
});

it("lucy-vton-3.5: virtual try-on (prompt)", async () => {
const result = await client.queue.submitAndPoll({
model: models.video("lucy-vton-3.5"),
prompt: "Wearing a red leather jacket",
data: videoBlob,
seed: 42,
});

await expectResult(result, "lucy-vton-3.5-prompt", ".mp4");
});

it("lucy-vton-3.5: virtual try-on (reference_image)", async () => {
const result = await client.queue.submitAndPoll({
model: models.video("lucy-vton-3.5"),
prompt: "",
reference_image: imageBlob,
data: videoBlob,
seed: 42,
});

await expectResult(result, "lucy-vton-3.5-reference_image", ".mp4");
});

// Deprecated video model names (aliases)
it("lucy-restyle-v2v (deprecated): video restyling", async () => {
const result = await client.queue.submitAndPoll({
Expand Down
35 changes: 28 additions & 7 deletions packages/sdk/tests/unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2435,6 +2435,7 @@ describe("Canonical Model Names", () => {
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
"lucy-restyle-2",
]);
expect(canonicalVideoModels.options).toEqual([
Expand All @@ -2443,6 +2444,7 @@ describe("Canonical Model Names", () => {
"lucy-2.5",
"lucy-vton-2",
"lucy-vton-3",
"lucy-vton-3.5",
"lucy-restyle-2",
]);
expect(canonicalImageModels.options).toEqual(["lucy-image-2"]);
Expand Down Expand Up @@ -2502,7 +2504,7 @@ describe("Canonical Model Names", () => {
it("lists all models when called without options", () => {
const listedModels = listModels();

expect(listedModels).toHaveLength(25);
expect(listedModels).toHaveLength(27);
expect(listedModels.some((model) => model.kind === "realtime" && model.name === "lucy-2.1")).toBe(true);
expect(listedModels.some((model) => model.kind === "video" && model.name === "lucy-clip")).toBe(true);
expect(listedModels.some((model) => model.kind === "image" && model.name === "lucy-image-2")).toBe(true);
Expand Down Expand Up @@ -2595,6 +2597,15 @@ describe("Canonical Model Names", () => {
expect(model.height).toBe(624);
});

it("lucy-vton-3.5 canonical name works", () => {
const model = models.realtime("lucy-vton-3.5");
expect(model.name).toBe("lucy-vton-3.5");
expect(model.urlPath).toBe("/v1/stream");
expect(model.fps).toEqual({ ideal: 30, max: 30 });
expect(model.width).toBe(1280);
expect(model.height).toBe(720);
});

it("lucy-restyle-2 canonical name works", () => {
const model = models.realtime("lucy-restyle-2");
expect(model.name).toBe("lucy-restyle-2");
Expand Down Expand Up @@ -2648,6 +2659,16 @@ describe("Canonical Model Names", () => {
expect(model.height).toBe(624);
});

it("lucy-vton-3.5 as video model works", () => {
const model = models.video("lucy-vton-3.5");
expect(model.name).toBe("lucy-vton-3.5");
expect(model.urlPath).toBe("/v1/generate/lucy-vton-3.5");
expect(model.queueUrlPath).toBe("/v1/jobs/lucy-vton-3.5");
expect(model.fps).toBe(20);
expect(model.width).toBe(1280);
expect(model.height).toBe(720);
});

it("lucy-restyle-2 as video model works", () => {
const model = models.video("lucy-restyle-2");
expect(model.name).toBe("lucy-restyle-2");
Expand Down Expand Up @@ -2675,13 +2696,13 @@ describe("Canonical Model Names", () => {
expect(model.height).toBe(624);
});

it("lucy-vton-latest works as realtime model and resolves server-side to lucy-vton-3", () => {
it("lucy-vton-latest works as realtime model and resolves server-side to lucy-vton-3.5", () => {
const model = models.realtime("lucy-vton-latest");
expect(model.name).toBe("lucy-vton-latest");
expect(model.urlPath).toBe("/v1/stream");
expect(model.fps).toEqual({ ideal: 30, max: 30 });
expect(model.width).toBe(1088);
expect(model.height).toBe(624);
expect(model.width).toBe(1280);
expect(model.height).toBe(720);
});

it("lucy-restyle-latest works as realtime model", () => {
Expand All @@ -2703,14 +2724,14 @@ describe("Canonical Model Names", () => {
expect(model.height).toBe(624);
});

it("lucy-vton-latest works as video model and resolves server-side to lucy-vton-3", () => {
it("lucy-vton-latest works as video model and resolves server-side to lucy-vton-3.5", () => {
const model = models.video("lucy-vton-latest");
expect(model.name).toBe("lucy-vton-latest");
expect(model.urlPath).toBe("/v1/generate/lucy-vton-latest");
expect(model.queueUrlPath).toBe("/v1/jobs/lucy-vton-latest");
expect(model.fps).toBe(20);
expect(model.width).toBe(1088);
expect(model.height).toBe(624);
expect(model.width).toBe(1280);
expect(model.height).toBe(720);
});

it("lucy-restyle-latest works as video model", () => {
Expand Down
Loading