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
41 changes: 1 addition & 40 deletions decart/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
RealTimeModels = Literal[
# Canonical names
"lucy",
"lucy-2",
"lucy-2.1",
"lucy-2.1-vton",
"lucy-restyle",
Expand All @@ -22,13 +21,11 @@
"mirage",
"mirage_v2",
"lucy_v2v_720p_rt",
"lucy_2_rt",
"live_avatar",
]
VideoModels = Literal[
# Canonical names
"lucy-clip",
"lucy-2",
"lucy-2.1",
"lucy-2.1-vton",
"lucy-restyle-2",
Expand All @@ -42,7 +39,6 @@
# Deprecated names
"lucy-pro-v2v",
"lucy-restyle-v2v",
"lucy-2-v2v",
]
ImageModels = Literal[
# Canonical names
Expand All @@ -59,12 +55,10 @@
"mirage": "lucy-restyle",
"mirage_v2": "lucy-restyle-2",
"lucy_v2v_720p_rt": "lucy",
"lucy_2_rt": "lucy-2",
"live_avatar": "live-avatar",
# Video aliases
"lucy-pro-v2v": "lucy-clip",
"lucy-restyle-v2v": "lucy-restyle-2",
"lucy-2-v2v": "lucy-2",
# Image aliases
"lucy-pro-i2i": "lucy-image-2",
}
Expand Down Expand Up @@ -163,7 +157,7 @@ def validate_prompt_or_reference_image(self) -> "VideoRestyleInput":


class VideoEdit2Input(DecartBaseModel):
"""Input for lucy-2-v2v model.
"""Input for Lucy 2.1 video editing models.

Prompt is required but can be an empty string.
Optional reference_image can also be provided.
Expand Down Expand Up @@ -201,14 +195,6 @@ class ImageToImageInput(DecartBaseModel):
height=704,
input_schema=BaseModel,
),
"lucy-2": ModelDefinition(
name="lucy-2",
url_path="/v1/stream",
fps=20,
width=1280,
height=720,
input_schema=BaseModel,
),
"lucy-2.1": ModelDefinition(
name="lucy-2.1",
url_path="/v1/stream",
Expand Down Expand Up @@ -299,14 +285,6 @@ class ImageToImageInput(DecartBaseModel):
height=704,
input_schema=BaseModel,
),
"lucy_2_rt": ModelDefinition(
name="lucy_2_rt",
url_path="/v1/stream",
fps=20,
width=1280,
height=720,
input_schema=BaseModel,
),
"live_avatar": ModelDefinition(
name="live_avatar",
url_path="/v1/stream",
Expand All @@ -326,14 +304,6 @@ class ImageToImageInput(DecartBaseModel):
height=704,
input_schema=VideoToVideoInput,
),
"lucy-2": ModelDefinition(
name="lucy-2",
url_path="/v1/jobs/lucy-2",
fps=20,
width=1280,
height=720,
input_schema=VideoEdit2Input,
),
"lucy-2.1": ModelDefinition(
name="lucy-2.1",
url_path="/v1/jobs/lucy-2.1",
Expand Down Expand Up @@ -424,14 +394,6 @@ class ImageToImageInput(DecartBaseModel):
height=704,
input_schema=VideoRestyleInput,
),
"lucy-2-v2v": ModelDefinition(
name="lucy-2-v2v",
url_path="/v1/jobs/lucy-2-v2v",
fps=20,
width=1280,
height=720,
input_schema=VideoEdit2Input,
),
},
"image": {
# Canonical names
Expand Down Expand Up @@ -483,7 +445,6 @@ def video(model: VideoModels) -> VideoModelDefinition:

Available models:
- "lucy-clip" - Video-to-video
- "lucy-2" - Video editing with reference image support
- "lucy-2.1" - Video editing (newer, higher quality)
- "lucy-restyle-2" - Video restyling with prompt or reference image
- "lucy-motion" - Image-to-motion-video
Expand Down
4 changes: 2 additions & 2 deletions decart/tokens/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class TokensClient:
# With expiry, model restrictions, and constraints:
token = await client.tokens.create(
expires_in=120,
allowed_models=["lucy-2"],
allowed_models=["lucy-2.1"],
constraints={"realtime": {"maxSessionDuration": 300}},
)
```
Expand Down Expand Up @@ -70,7 +70,7 @@ async def create(
token = await client.tokens.create(
metadata={"role": "viewer"},
expires_in=120,
allowed_models=["lucy-2"],
allowed_models=["lucy-2.1"],
constraints={"realtime": {"maxSessionDuration": 300}},
)
```
Expand Down
2 changes: 1 addition & 1 deletion examples/realtime_synthetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async def main():
print("Creating synthetic video track...")
video_track = SyntheticVideoTrack()

model = models.realtime("lucy-2")
model = models.realtime("lucy-2.1")
print(f"Using model: {model.name}")
print(f"Model config - FPS: {model.fps}, Size: {model.width}x{model.height}")

Expand Down
7 changes: 3 additions & 4 deletions playground/playground.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,13 @@ def _check_deps() -> None:

REALTIME_MODELS = [
"lucy",
"lucy-2",
"lucy-2.1",
"lucy-2.1-vton",
"lucy-restyle",
"lucy-restyle-2",
"live-avatar",
]
CAMERA_MODELS = {"lucy", "lucy-2", "lucy-2.1", "lucy-2.1-vton", "lucy-restyle", "lucy-restyle-2"}
CAMERA_MODELS = {"lucy", "lucy-2.1", "lucy-2.1-vton", "lucy-restyle", "lucy-restyle-2"}
AVATAR_MODELS = {"live-avatar"}

BANNER = """
Expand Down Expand Up @@ -188,7 +187,7 @@ def parse_args() -> argparse.Namespace:
%(prog)s --model lucy-restyle-2 --prompt "Anime style"
%(prog)s --model live-avatar --image avatar.png
%(prog)s --model live-avatar --image avatar.png --audio speech.mp3
%(prog)s --model lucy-2 --image ref.png --prompt "Lego World"
%(prog)s --model lucy-2.1 --image ref.png --prompt "Lego World"
""",
)
p.add_argument("--model", "-m", choices=REALTIME_MODELS, help="Model name")
Expand All @@ -209,7 +208,7 @@ def select_model_interactive() -> str:
note = ""
if name in AVATAR_MODELS:
note = " (requires --image)"
elif name in ("lucy-2", "lucy-2.1", "lucy-2.1-vton", "lucy-restyle-2"):
elif name in ("lucy-2.1", "lucy-2.1-vton", "lucy-restyle-2"):
note = " (supports reference image)"
print(f" {i}. {name}{note}")

Expand Down
22 changes: 0 additions & 22 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ def test_canonical_realtime_models() -> None:
assert model.width == 1280
assert model.height == 704

model = models.realtime("lucy-2")
assert model.name == "lucy-2"
assert model.fps == 20
assert model.width == 1280
assert model.height == 720

model = models.realtime("lucy-2.1")
assert model.name == "lucy-2.1"
assert model.fps == 20
Expand Down Expand Up @@ -84,13 +78,6 @@ def test_canonical_video_models() -> None:
assert model.name == "lucy-clip"
assert model.url_path == "/v1/jobs/lucy-clip"

model = models.video("lucy-2")
assert model.name == "lucy-2"
assert model.url_path == "/v1/jobs/lucy-2"
assert model.fps == 20
assert model.width == 1280
assert model.height == 720

model = models.video("lucy-2.1")
assert model.name == "lucy-2.1"
assert model.url_path == "/v1/jobs/lucy-2.1"
Expand Down Expand Up @@ -133,15 +120,6 @@ def test_deprecated_video_models() -> None:
assert len(w) == 1
assert "lucy-restyle-2" in str(w[0].message)

_warned_aliases.clear()

with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
model = models.video("lucy-2-v2v")
assert model.name == "lucy-2-v2v"
assert len(w) == 1
assert '"lucy-2"' in str(w[0].message)


def test_canonical_image_models() -> None:
model = models.image("lucy-image-2")
Expand Down
26 changes: 13 additions & 13 deletions tests/test_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,70 +269,70 @@ async def test_queue_includes_user_agent_header() -> None:
assert headers["User-Agent"].startswith("decart-python-sdk/")


# Tests for lucy-2
# Tests for lucy-2.1


@pytest.mark.asyncio
async def test_queue_lucy2_v2v_with_prompt() -> None:
async def test_queue_lucy21_v2v_with_prompt() -> None:
client = DecartClient(api_key="test-key")

with patch("decart.queue.client.submit_job") as mock_submit:
mock_submit.return_value = MagicMock(job_id="job-lucy2", status="pending")
mock_submit.return_value = MagicMock(job_id="job-lucy21", status="pending")

job = await client.queue.submit(
{
"model": models.video("lucy-2"),
"model": models.video("lucy-2.1"),
"prompt": "Restyle the scene with softer contrast and warmer highlights",
"data": b"fake video data",
"enhance_prompt": True,
"seed": 42,
}
)

assert job.job_id == "job-lucy2"
assert job.job_id == "job-lucy21"
assert job.status == "pending"
mock_submit.assert_called_once()


@pytest.mark.asyncio
async def test_queue_lucy2_v2v_with_empty_prompt_and_reference_image() -> None:
async def test_queue_lucy21_v2v_with_empty_prompt_and_reference_image() -> None:
client = DecartClient(api_key="test-key")

with patch("decart.queue.client.submit_job") as mock_submit:
mock_submit.return_value = MagicMock(job_id="job-lucy2-ref", status="pending")
mock_submit.return_value = MagicMock(job_id="job-lucy21-ref", status="pending")

job = await client.queue.submit(
{
"model": models.video("lucy-2"),
"model": models.video("lucy-2.1"),
"prompt": "",
"reference_image": b"fake image data",
"data": b"fake video data",
}
)

assert job.job_id == "job-lucy2-ref"
assert job.job_id == "job-lucy21-ref"
assert job.status == "pending"
mock_submit.assert_called_once()


@pytest.mark.asyncio
async def test_queue_lucy2_v2v_with_both_prompt_and_reference_image() -> None:
async def test_queue_lucy21_v2v_with_both_prompt_and_reference_image() -> None:
client = DecartClient(api_key="test-key")

with patch("decart.queue.client.submit_job") as mock_submit:
mock_submit.return_value = MagicMock(job_id="job-lucy2-both", status="pending")
mock_submit.return_value = MagicMock(job_id="job-lucy21-both", status="pending")

job = await client.queue.submit(
{
"model": models.video("lucy-2"),
"model": models.video("lucy-2.1"),
"prompt": "Transform the scene",
"reference_image": b"fake image data",
"data": b"fake video data",
"seed": 123,
}
)

assert job.job_id == "job-lucy2-both"
assert job.job_id == "job-lucy21-both"
assert job.status == "pending"
mock_submit.assert_called_once()

Expand Down
8 changes: 4 additions & 4 deletions tests/test_realtime_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ def test_realtime_models_available():
assert model2.height == 704
assert model2.url_path == "/v1/stream"

model2 = models.realtime("lucy-2")
assert model2.name == "lucy-2"
model2 = models.realtime("lucy-2.1")
assert model2.name == "lucy-2.1"
Comment thread
cursor[bot] marked this conversation as resolved.
assert model2.fps == 20
assert model2.width == 1280
assert model2.height == 720
assert model2.width == 1088
assert model2.height == 624
assert model2.url_path == "/v1/stream"


Expand Down
12 changes: 6 additions & 6 deletions tests/test_tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,10 @@ async def test_create_token_with_allowed_models() -> None:
)

with patch.object(client, "_get_session", AsyncMock(return_value=mock_session)):
await client.tokens.create(allowed_models=["lucy-2"])
await client.tokens.create(allowed_models=["lucy-2.1"])

call_kwargs = mock_session.post.call_args
assert call_kwargs.kwargs["json"] == {"allowedModels": ["lucy-2"]}
assert call_kwargs.kwargs["json"] == {"allowedModels": ["lucy-2.1"]}


@pytest.mark.asyncio
Expand Down Expand Up @@ -199,7 +199,7 @@ async def test_create_token_with_all_v2_fields() -> None:
return_value={
"apiKey": "ek_test123",
"expiresAt": "2024-12-15T12:10:00Z",
"permissions": {"models": ["lucy-2"]},
"permissions": {"models": ["lucy-2.1"]},
"constraints": {"realtime": {"maxSessionDuration": 120}},
}
)
Expand All @@ -213,19 +213,19 @@ async def test_create_token_with_all_v2_fields() -> None:
result = await client.tokens.create(
metadata={"role": "viewer"},
expires_in=120,
allowed_models=["lucy-2"],
allowed_models=["lucy-2.1"],
constraints={"realtime": {"maxSessionDuration": 120}},
)

assert result.api_key == "ek_test123"
assert result.expires_at == "2024-12-15T12:10:00Z"
assert result.permissions == {"models": ["lucy-2"]}
assert result.permissions == {"models": ["lucy-2.1"]}
assert result.constraints == {"realtime": {"maxSessionDuration": 120}}

call_kwargs = mock_session.post.call_args
assert call_kwargs.kwargs["json"] == {
"metadata": {"role": "viewer"},
"expiresIn": 120,
"allowedModels": ["lucy-2"],
"allowedModels": ["lucy-2.1"],
"constraints": {"realtime": {"maxSessionDuration": 120}},
}
Loading