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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
python-version: ${{ inputs.python-version || '3.12' }}
- run: |
if [ -d "packages" ]; then
uv run mypy -p celeste && uv run mypy tests/ && uv run mypy packages/image-generation packages/text-generation
uv run mypy -p celeste && uv run mypy tests/ && uv run mypy packages/image-generation packages/text-generation packages/video-generation
else
uv run mypy -p celeste && uv run mypy tests/
fi
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ format:

# Type checking (fail fast on any error)
typecheck:
@uv run mypy -p celeste && uv run mypy tests/ && uv run mypy packages/image-generation packages/text-generation
@uv run mypy -p celeste && uv run mypy tests/ && uv run mypy packages/image-generation packages/text-generation packages/video-generation

# Testing
test:
Expand Down
4 changes: 2 additions & 2 deletions packages/image-generation/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "celeste-image-generation"
version = "0.2.2"
description = "Type-safe image generation for Celeste AI. Unified interface for OpenAI, Google, ByteDance, and more"
version = "0.2.7"
description = "Image generation package for Celeste AI. Unified interface for all providers"
authors = [{name = "Kamilbenkirane", email = "kamil@withceleste.ai"}]
readme = "README.md"
license = {text = "Apache-2.0"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ImageGenerationStream(
):
"""Streaming for image generation."""

def _parse_output(
def _parse_output( # type: ignore[override]
self,
chunks: list[ImageGenerationChunk],
**parameters: Unpack[ImageGenerationParameters],
Expand Down
4 changes: 2 additions & 2 deletions packages/text-generation/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
name = "celeste-text-generation"
version = "0.2.2"
description = "Type-safe text generation for Celeste AI. Unified interface for OpenAI, Anthropic, Google, Mistral, Cohere, and more"
version = "0.2.7"
description = "Text generation package for Celeste AI. Unified interface for all providers"
authors = [{name = "Kamilbenkirane", email = "kamil@withceleste.ai"}]
readme = "README.md"
license = {text = "Apache-2.0"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TextGenerationStream(
def _parse_chunk(self, event: dict[str, Any]) -> TextGenerationChunk | None:
"""Parse SSE event into Chunk (provider-specific)."""

def _parse_output(
def _parse_output( # type: ignore[override]
self,
chunks: list[TextGenerationChunk],
**parameters: Unpack[TextGenerationParameters],
Expand Down
79 changes: 79 additions & 0 deletions packages/video-generation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<div align="center">

# <img src="../../logo.svg" width="48" height="48" alt="Celeste Logo" style="vertical-align: middle;"> Celeste Video Generation

**Video Generation capability for Celeste AI**

[![Python](https://img.shields.io/badge/Python-3.12+-blue?style=for-the-badge)](https://www.python.org/)
[![License](https://img.shields.io/badge/License-Apache_2.0-red?style=for-the-badge)](../../LICENSE)

[Quick Start](#-quick-start) • [Documentation](https://withceleste.ai/docs) • [Request Provider](https://github.com/withceleste/celeste-python/issues/new)

</div>

---

## 🚀 Quick Start

```python
from celeste import create_client, Capability, Provider

client = create_client(
capability=Capability.VIDEO_GENERATION,
provider=Provider.OPENAI,
)

response = await client.generate(prompt="A cinematic video of a sunset over mountains")
print(response.content)
```

**Install:**
```bash
uv add "celeste-ai[video-generation]"
```

---

## Supported Providers


<div align="center">

<img src="https://www.google.com/s2/favicons?domain=seed.bytedance.com&sz=64" width="64" height="64" alt="ByteDance" title="ByteDance">
<img src="https://www.google.com/s2/favicons?domain=openai.com&sz=64" width="64" height="64" alt="OpenAI" title="OpenAI">
<img src="https://www.google.com/s2/favicons?domain=google.com&sz=64" width="64" height="64" alt="Google" title="Google">


**Missing a provider?** [Request it](https://github.com/withceleste/celeste-python/issues/new) – ⚡ **we ship fast**.

</div>

---

**Streaming**: ❌ Not Supported

**Parameters**: See [API Documentation](https://withceleste.ai/docs/api) for full parameter reference.

---

## 🤝 Contributing

See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.

**Request a provider:** [GitHub Issues](https://github.com/withceleste/celeste-python/issues/new)

---

## 📄 License

Apache 2.0 License – see [LICENSE](../../LICENSE) for details.

---

<div align="center">

**[Get Started](https://withceleste.ai/docs/quickstart)** • **[Documentation](https://withceleste.ai/docs)** • **[GitHub](https://github.com/withceleste/celeste-python)**

Made with ❤️ by developers tired of framework lock-in

</div>
42 changes: 42 additions & 0 deletions packages/video-generation/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[project]
name = "celeste-video-generation"
version = "0.2.7"
description = "Video generation package for Celeste AI. Unified interface for all providers"
authors = [{name = "Kamilbenkirane", email = "kamil@withceleste.ai"}]
readme = "README.md"
license = {text = "Apache-2.0"}
requires-python = ">=3.12"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Operating System :: OS Independent",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Typing :: Typed",
]
keywords = ["ai", "video-generation", "sora", "runway", "openai", "google", "video-ai"]
dependencies = [
"pillow>=10.0.0",
]

[project.urls]
Homepage = "https://withceleste.ai"
Documentation = "https://withceleste.ai/docs"
Repository = "https://github.com/withceleste/celeste-python"
Issues = "https://github.com/withceleste/celeste-python/issues"

[tool.uv.sources]
celeste-ai = { workspace = true }

[project.entry-points."celeste.packages"]
video-generation = "celeste_video_generation:register_package"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/celeste_video_generation"]
29 changes: 29 additions & 0 deletions packages/video-generation/src/celeste_video_generation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Celeste video generation capability."""


def register_package() -> None:
"""Register video generation package (client and models)."""
from celeste.client import register_client
from celeste.core import Capability
from celeste.models import register_models
from celeste_video_generation.models import MODELS
from celeste_video_generation.providers import PROVIDERS

for provider, client_class in PROVIDERS:
register_client(Capability.VIDEO_GENERATION, provider, client_class)

register_models(MODELS, capability=Capability.VIDEO_GENERATION)


from celeste_video_generation.io import ( # noqa: E402
VideoGenerationInput,
VideoGenerationOutput,
VideoGenerationUsage,
)

__all__ = [
"VideoGenerationInput",
"VideoGenerationOutput",
"VideoGenerationUsage",
"register_package",
]
64 changes: 64 additions & 0 deletions packages/video-generation/src/celeste_video_generation/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Base client for video generation."""

from abc import abstractmethod
from typing import Any, Unpack

import httpx

from celeste.artifacts import VideoArtifact
from celeste.client import Client
from celeste_video_generation.io import (
VideoGenerationInput,
VideoGenerationOutput,
VideoGenerationUsage,
)
from celeste_video_generation.parameters import VideoGenerationParameters


class VideoGenerationClient(
Client[VideoGenerationInput, VideoGenerationOutput, VideoGenerationParameters]
):
"""Client for video generation operations."""

@abstractmethod
def _init_request(self, inputs: VideoGenerationInput) -> dict[str, Any]:
"""Initialize provider-specific request structure."""

@abstractmethod
def _parse_usage(self, response_data: dict[str, Any]) -> VideoGenerationUsage:
"""Parse usage information from provider response."""

@abstractmethod
def _parse_content(
self,
response_data: dict[str, Any],
**parameters: Unpack[VideoGenerationParameters],
) -> VideoArtifact:
"""Parse content from provider response."""

def _create_inputs(
self,
prompt: str,
**parameters: Unpack[VideoGenerationParameters],
) -> VideoGenerationInput:
"""Map positional arguments to Input type."""
return VideoGenerationInput(prompt=prompt)

@classmethod
def _output_class(cls) -> type[VideoGenerationOutput]:
"""Return the Output class for this client."""
return VideoGenerationOutput

def _build_metadata(self, response_data: dict[str, Any]) -> dict[str, Any]:
"""Build metadata dictionary from response data."""
metadata = super()._build_metadata(response_data)
metadata["raw_response"] = response_data
return metadata

@abstractmethod
async def _make_request(
self,
request_body: dict[str, Any],
**parameters: Unpack[VideoGenerationParameters],
) -> httpx.Response:
"""Make HTTP request(s) and return response object."""
33 changes: 33 additions & 0 deletions packages/video-generation/src/celeste_video_generation/io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Input and output types for video generation."""

from celeste.artifacts import VideoArtifact
from celeste.io import Input, Output, Usage


class VideoGenerationInput(Input):
"""Input for video generation operations."""

prompt: str


class VideoGenerationUsage(Usage):
"""Video generation usage metrics.

All fields optional since providers vary.
"""

total_tokens: int | None = None
billing_units: float | None = None


class VideoGenerationOutput(Output[VideoArtifact]):
"""Output with VideoArtifact content."""

pass


__all__ = [
"VideoGenerationInput",
"VideoGenerationOutput",
"VideoGenerationUsage",
]
14 changes: 14 additions & 0 deletions packages/video-generation/src/celeste_video_generation/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Model definitions for video generation."""

from celeste import Model
from celeste_video_generation.providers.bytedance.models import (
MODELS as BYTEDANCE_MODELS,
)
from celeste_video_generation.providers.google.models import MODELS as GOOGLE_MODELS
from celeste_video_generation.providers.openai.models import MODELS as OPENAI_MODELS

MODELS: list[Model] = [
*BYTEDANCE_MODELS,
*GOOGLE_MODELS,
*OPENAI_MODELS,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Parameters for video generation."""

from enum import StrEnum

from celeste.artifacts import ImageArtifact
from celeste.parameters import Parameters


class VideoGenerationParameter(StrEnum):
"""Unified parameter names for video generation capability."""

ASPECT_RATIO = "aspect_ratio"
RESOLUTION = "resolution"
DURATION = "duration"
REFERENCE_IMAGES = "reference_images"
FIRST_FRAME = "first_frame"
LAST_FRAME = "last_frame"


class VideoGenerationParameters(Parameters):
"""Parameters for video generation."""

aspect_ratio: str | None
resolution: str | None
duration: int | None
reference_images: list[ImageArtifact] | None
first_frame: ImageArtifact | None
last_frame: ImageArtifact | None
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Provider implementations for video generation."""

from celeste import Client, Provider

__all__ = ["PROVIDERS"]


def _get_providers() -> list[tuple[Provider, type[Client]]]:
"""Lazy-load providers."""
# Import clients directly from .client modules to avoid __init__.py imports
from celeste_video_generation.providers.bytedance.client import (
ByteDanceVideoGenerationClient,
)
from celeste_video_generation.providers.google.client import (
GoogleVideoGenerationClient,
)
from celeste_video_generation.providers.openai.client import (
OpenAIVideoGenerationClient,
)

return [
(Provider.BYTEDANCE, ByteDanceVideoGenerationClient),
(Provider.GOOGLE, GoogleVideoGenerationClient),
(Provider.OPENAI, OpenAIVideoGenerationClient),
]


PROVIDERS: list[tuple[Provider, type[Client]]] = _get_providers()
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ByteDance Video Generation Provider

## Credentials

**Environment variable:** `BYTEDANCE_API_KEY`

**Setup:**
1. Register at [console.byteplus.com](https://console.byteplus.com)
2. Activate model in ModelArk section
3. Generate API key with video generation permissions
4. Set environment variable: `export BYTEDANCE_API_KEY="your-key"`

**Note:** Models must be activated in BytePlus console before use. If you get a 404 error, activate the model or use an Endpoint ID (`ep-xxx`) instead of Model ID.
Loading
Loading