Skip to main content

Maintain Visual Consistency with Reusable Video Characters

Sameer Kankute
SWE @ LiteLLM (LLM Translation)
Krrish Dholakia
CEO, LiteLLM
Ishaan Jaff
CTO, LiteLLM

Build branded video sequences with consistent characters using the LiteLLM Video Characters API. Create once, reuse everywhere.

Proxy Setup​

model_list:
- model_name: sora-2
litellm_params:
model: openai/sora-2
api_key: os.environ/OPENAI_API_KEY
- model_name: sora-2-pro
litellm_params:
model: openai/sora-2-pro
api_key: os.environ/OPENAI_API_KEY
litellm --config /path/to/config.yaml

SDK & Proxy Usage​

Create a Character​

Upload a short video clip to create a reusable character asset.

from litellm import avideo_create_character

# Create character from video
character = await avideo_create_character(
name="Mossy", # Character name (use in prompts)
video=open("character.mp4", "rb"), # Short 2-4 second clip
custom_llm_provider="openai",
model="sora-2"
)

print(f"Character ID: {character.id}")
# Output: Character ID: char_abc123def456

Proxy endpoint:

curl -X POST "http://localhost:4000/v1/videos/characters" \
-H "Authorization: Bearer sk-litellm-key" \
-F "video=@character.mp4" \
-F "name=Mossy"

Generate Video with Character​

Include the character ID in the characters array and mention the character name in your prompt.

from litellm import avideo

# Create video using the character
video = await avideo(
model="sora-2",
prompt="A cinematic tracking shot of Mossy weaving through a lantern-lit market at dusk, looking around curiously.",
characters=[{"id": "char_abc123def456"}],
seconds="8",
size="1280x720"
)

print(f"Video ID: {video.id}")

Proxy endpoint:

curl -X POST "http://localhost:4000/v1/videos" \
-H "Authorization: Bearer sk-litellm-key" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
"prompt": "Mossy dances through a meadow of glowing flowers.",
"characters": [{"id": "char_abc123def456"}],
"seconds": "8",
"size": "1280x720"
}'

Retrieve Character Info​

Get metadata and status for an uploaded character.

from litellm import avideo_get_character

character = await avideo_get_character(
character_id="char_abc123def456",
custom_llm_provider="openai"
)

print(f"Character: {character.name}, Created: {character.created_at}")

Edit Video with Character​

Preserve the character while making targeted edits to the scene.

from litellm import avideo_edit

# Edit existing video with character
edited = await avideo_edit(
video_id="video_xyz123",
prompt="Shift the lighting to warm golden hour, add wind effects to character's fur.",
custom_llm_provider="openai"
)

Extend Video with Character​

Continue a completed video with the same character for longer sequences.

from litellm import avideo_extension

# Extend video by up to 20 seconds
extended = await avideo_extension(
video_id="video_xyz123",
prompt="Mossy walks towards the camera, waves, and smiles warmly.",
seconds="8",
custom_llm_provider="openai"
)

Best Practices​

Character Uploads​

  • Optimal duration: 2-4 seconds for best consistency
  • Aspect ratio: Match the target video resolution (16:9, 9:16, or 1:1)
  • Resolution: 720p to 1080p
  • Isolation: Show the character clearly against a distinct background
  • Movement: Include natural motion (walk, turn, gesture) to establish the character

Prompting​

āŒ Avoid:

"Create a video with a character that looks like Mossy"

āœ… Do this:

"Mossy the moss-covered teapot mascot weaves through a lantern-lit market at dusk, 
looking curious and friendly."

Always mention the character name verbatim in your prompt. The character ID alone won't reliably preserve the asset.

Character Limits​

  • Per video: Up to 2 characters in a single generation
  • Extensions: 1 character per extension (characters not supported in extensions)
  • Consistency: Best results when character occupies 30-60% of frame

Full Example Workflow​

Create a branded video series with a consistent mascot character.

Python workflow example
import asyncio
from litellm import (
avideo_create_character,
avideo,
avideo_extension,
avideo_edit,
avideo_get_character
)

async def create_branded_series():
# Step 1: Create character asset
print("Creating character...")
character = await avideo_create_character(
name="Luna",
video=open("luna_intro.mp4", "rb"),
custom_llm_provider="openai"
)
char_id = character.id
print(f"āœ“ Character created: {char_id}")

# Step 2: Generate first scene
print("Generating opening scene...")
scene1 = await avideo(
model="sora-2",
prompt="Luna the magical fox dancing through a cosmic forest, stars trailing her movement.",
characters=[{"id": char_id}],
seconds="8",
size="1280x720"
)
print(f"āœ“ Scene 1: {scene1.id} ({scene1.status})")

# Wait for completion (in production, use webhooks)
while scene1.status in ("queued", "in_progress"):
await asyncio.sleep(5)
scene1 = await avideo_get_character(char_id) # refresh

# Step 3: Extend with same character
print("Extending scene...")
scene2 = await avideo_extension(
video_id=scene1.id,
prompt="Luna leaps into the air, transforms into stardust, and reforms on a moonlit cliff.",
seconds="6"
)
print(f"āœ“ Scene 2 (extension): {scene2.id}")

# Step 4: Edit for color grading
print("Applying color grade...")
scene1_graded = await avideo_edit(
video_id=scene1.id,
prompt="Shift palette to cool blues and purples with enhanced glow effects around Luna."
)
print(f"āœ“ Scene 1 graded: {scene1_graded.id}")

print("\nāœ“ Branded series created with consistent Luna character!")

asyncio.run(create_branded_series())

FAQ​

Q: Can I use real people as characters?
A: Character uploads depicting human likeness are blocked by default. Contact OpenAI sales for eligibility.

Q: What happens if the character aspect ratio doesn't match the video size?
A: The character may appear stretched or distorted. Upload character videos matching your target aspect ratio.

Q: Can I use the same character in extensions?
A: Extensions don't currently support character preservation. Use image references or re-upload the character from the extended video.

Q: How long do characters persist?
A: Characters are stored with your account indefinitely. You can retrieve and reuse them anytime with GET /v1/videos/characters/{character_id}.

Q: Can I combine characters with image references?
A: Yes! Use input_reference (to set the opening frame) alongside characters (for reusable assets) in the same generation.

Q: What's the difference between characters and input_reference?
A: input_reference conditions a single generation's opening frame. characters are reusable assets you reference across multiple generations for consistency.

Q: How many characters can I upload?
A: No limit. Upload as many character assets as you need for your project.

Q: Can I update or delete a character?
A: Character assets are immutable. To modify an asset, upload a new character and reference the new ID in future generations.

Troubleshooting​

Character looks distorted in output

  • Verify the character video matches the target resolution
  • Re-upload with matching aspect ratio (16:9, 9:16, or 1:1)

Character doesn't appear in generated video

  • Check that you included the character ID in the characters array
  • Verify the character name is mentioned in the prompt (verbatim)
  • Ensure character occupies meaningful screen space in your prompt description

Character upload fails

  • Maximum file size is typically 100MB
  • Use MP4 format, 2-4 seconds, 720p-1080p resolution
  • Ensure character is clearly visible and isolated

Next Steps​