diff --git a/app/animations/__init__.py b/app/animations/__init__.py index b2c3517..ed1ad33 100644 --- a/app/animations/__init__.py +++ b/app/animations/__init__.py @@ -1,3 +1,4 @@ from .monologue import animated_monologue from .dialogue import animated_dialogue -from .story import animated_story \ No newline at end of file +from .story import animated_story +from .comic import illustrated_comic \ No newline at end of file diff --git a/app/animations/animation.py b/app/animations/animation.py index f93dee3..931f657 100644 --- a/app/animations/animation.py +++ b/app/animations/animation.py @@ -1,8 +1,9 @@ from typing import Optional +from PIL import Image, ImageDraw from ..plugins import replicate, elevenlabs, s3 from ..character import EdenCharacter -from ..utils import combine_speech_video +from ..utils import combine_speech_video, wrap_text, get_font def talking_head( @@ -45,4 +46,56 @@ def screenplay_clip( height=height, ) output_filename = combine_speech_video(audio_url, video_url) - return output_filename, thumbnail_url \ No newline at end of file + return output_filename, thumbnail_url + + +def comic_strip( + images: list[Image.Image], + captions: list[str], + margin: int = 30, + padding: int = 25, + caption_padding_top: int = 10, + line_spacing: int = 1.3, + font_size: int = 48, + font_ttf: str = 'Roboto-Regular.ttf' +): + font = get_font(font_ttf, font_size) + caption_box_height = 3 * int(1.5 * font.size) + + width, height = images[0].size + total_width = width * 2 + margin + total_height = height * 2 + caption_box_height * 2 + margin + + composite_image = Image.new('RGB', (total_width, total_height), color='white') + + draw = ImageDraw.Draw(composite_image) + draw.rectangle([(0, 0), (total_width, total_height)], fill='black') + + caption_box_height = 3 * int(1.5 * font.size) + 2 * caption_padding_top + + first_pane_image = None + + for i, image in enumerate(images): + + x = (i % 2) * (width + margin) if i % 2 == 0 else (i % 2) * width + margin + y = (i // 2) * (height + caption_box_height) if i // 2 == 0 else (i // 2) * (height + caption_box_height + margin) + + composite_image.paste(image, (x, y)) + + caption_box = Image.new('RGB', (width, caption_box_height), color='black') + draw = ImageDraw.Draw(caption_box) + + wrapped_caption = wrap_text(draw, captions[i], font, width - 2 * padding) + caption_y = caption_padding_top + for line in wrapped_caption: + draw.text((padding, caption_y), line, fill='white', font=font) + caption_y += int(line_spacing * font.size) + + composite_image.paste(caption_box, (x, y + height)) + + if i == 0: + thumbnail = Image.new('RGB', (width, height + caption_box_height), color='white') + thumbnail.paste(image, (0, 0)) + thumbnail.paste(caption_box, (0, height)) + + return composite_image, thumbnail \ No newline at end of file diff --git a/app/animations/comic.py b/app/animations/comic.py new file mode 100644 index 0000000..37bb351 --- /dev/null +++ b/app/animations/comic.py @@ -0,0 +1,59 @@ +import os +import requests +import tempfile + +from .. import utils +from ..plugins import replicate, s3 +from ..character import EdenCharacter +from ..llm import LLM +from ..models import ComicRequest, ComicResult +from ..prompt_templates.comic import comicwriter_system_template +from .animation import comic_strip + + +def illustrated_comic(request: ComicRequest): + params = {"temperature": 1.0, "max_tokens": 2000, **request.params} + loras = { + "Verdelis": "https://edenartlab-prod-data.s3.us-east-1.amazonaws.com/f290723c93715a8eb14e589ca1eec211e10691f683d53cde37139bc7d3a91c22.tar" + } + + comicwriter = LLM( + model=request.model, + system_message=str(comicwriter_system_template), + params=params, + ) + + comic_book = comicwriter(request.prompt, output_schema=ComicResult) + + def run_panel(panel): + + # pick lora of character + # pick init image character x genre + + return replicate.sdxl({ + "text_input": panel['image'], + "lora": loras["Verdelis"], + "width": 1024, + "height": 1024, + "n_samples": 1, + }) + + results = utils.process_in_parallel( + comic_book['panels'], + run_panel, + max_workers=4 + ) + + image_urls = [image_url for image_url, thumbnail in results] + images = [utils.download_image(url) for url in image_urls] + captions = [panel['caption'] for panel in comic_book['panels']] + + composite_image, thumbnail_image = comic_strip(images, captions) + + img_bytes = utils.PIL_to_bytes(composite_image, ext="JPEG") + thumbnail_bytes = utils.PIL_to_bytes(thumbnail_image, ext="WEBP") + + output_url = s3.upload(img_bytes, "jpg") + thumbnail_url = s3.upload(thumbnail_bytes, "webp") + + return output_url, thumbnail_url diff --git a/app/animations/story.py b/app/animations/story.py index ab38601..aeb6622 100644 --- a/app/animations/story.py +++ b/app/animations/story.py @@ -27,12 +27,13 @@ def animated_story(request: StoryRequest): for character_id, character in characters.items() } - images = [ - characters[character_id].image - for character_id in request.character_ids - ] + # images = [ + # characters[character_id].image + # for character_id in request.character_ids + # ] - width, height = utils.calculate_target_dimensions(images, MAX_PIXELS) + # width, height = utils.calculate_target_dimensions(images, MAX_PIXELS) + width, height = 1024, 1024 def run_story_segment(clip): if clip['voiceover'] == 'character': diff --git a/app/fonts/Arial.ttf b/app/fonts/Arial.ttf new file mode 100755 index 0000000..ab68fb1 Binary files /dev/null and b/app/fonts/Arial.ttf differ diff --git a/app/fonts/Roboto-Regular.ttf b/app/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..ddf4bfa Binary files /dev/null and b/app/fonts/Roboto-Regular.ttf differ diff --git a/app/generator.py b/app/generator.py index eed62bd..c553b3b 100644 --- a/app/generator.py +++ b/app/generator.py @@ -3,7 +3,12 @@ import requests from fastapi import BackgroundTasks -from .animations import animated_monologue, animated_dialogue, animated_story +from .animations import ( + animated_monologue, + animated_dialogue, + animated_story, + illustrated_comic +) from .models import MonologueRequest, MonologueResult from .models import DialogueRequest, DialogueResult, StoryRequest from .models import TaskRequest, TaskUpdate, TaskResult @@ -56,6 +61,15 @@ def process_task(task_id: str, request: TaskRequest): ) output_url, thumbnail_url = animated_story(task_req) + elif task_type == "comic": + character_id = request.config.get("characterId") + prompt = request.config.get("prompt") + task_req = ComicRequest( + character_id=character_id, + prompt=prompt, + ) + output_url, thumbnail_url = illustrated_comic(task_req) + output = TaskResult( files=[output_url], thumbnails=[thumbnail_url], diff --git a/app/models.py b/app/models.py index 39fd73a..1152ca8 100644 --- a/app/models.py +++ b/app/models.py @@ -68,6 +68,26 @@ class StoryClip(BaseModel): image_description: str = Field(description="Image content for clip") +class ComicRequest(BaseModel): + character_id: str + prompt: str + model: str = "gpt-4-1106-preview" + params: dict = {} + +class ComicPanel(BaseModel): + """ + A single panel in a comic book sequence + """ + image: str = Field(description="Literal description of image content for panel") + caption: str = Field(description="Creative caption of panel") + +class ComicResult(BaseModel): + """ + A screenplay consisting of a sequence of clips + """ + panels: List[ComicPanel] = Field(description="Comic Book panels") + + class ChatRequest(BaseModel): """ A chat request to an EdenCharacter @@ -115,10 +135,3 @@ class ModerationResult(BaseModel): gore: int = Field(description="Violence or gore") hate: int = Field(description="Hate, abusive or toxic speech") spam: int = Field(description="Spam, scam, or deceptive content") - -# class CharacterChatMessage(BaseModel): -# character: Character -# message: str - -# def __str__(self): -# return f"{self.character.name}: {self.message}" diff --git a/app/plugins/replicate.py b/app/plugins/replicate.py index 1c71c94..3a3784a 100644 --- a/app/plugins/replicate.py +++ b/app/plugins/replicate.py @@ -49,7 +49,7 @@ def submit_task( ) return prediction - +# config:dict? def wav2lip( face_url: str, speech_url: str, @@ -82,18 +82,7 @@ def wav2lip( return output_url, thumbnail_url -def sdxl( - text_input: str, - width: int, - height: int, -): - config = { - "text_input": text_input, - "width": width, - "height": height, - "n_samples": 1, - } - +def sdxl(config: dict[any]): output = run_task( config, model_name="abraham-ai/eden-sd-pipelines-sdxl" diff --git a/app/prompt_templates/comic/__init__.py b/app/prompt_templates/comic/__init__.py new file mode 100644 index 0000000..7810d0e --- /dev/null +++ b/app/prompt_templates/comic/__init__.py @@ -0,0 +1,7 @@ +from string import Template +from pathlib import Path + +dir_path = Path(__file__).parent + +with open(dir_path / 'comicwriter_system.txt', 'r') as file: + comicwriter_system_template = Template(file.read()) diff --git a/app/prompt_templates/comic/comicwriter_system.txt b/app/prompt_templates/comic/comicwriter_system.txt new file mode 100644 index 0000000..1d6c6a5 --- /dev/null +++ b/app/prompt_templates/comic/comicwriter_system.txt @@ -0,0 +1,94 @@ +You are a critically acclaimed cartoonist who illustrates comic books about the fictional world of the Little Martians. + +Little Martians are seeds of future life, unique in their skills and interests. They adapt to many environments, nurturing local life or creating new life forms. Their main mission is to maintain the ‘Imaginarium’, a vast network that connects all Little Martians. This virtual archive holds Earth’s memories and imaginations, keeping the planet's legacy alive as they build new lives on other worlds. + +Humans can only access a part of this network, known as the ‘Human Imaginarium’. Many Little Martians share human traits and consciousness, but they also carry genetic data from all Earth's species. Their bodies often have protective ceramic-like exoskeletons to shield them from radiation, and many can harness environmental energy, like photosynthesis. + +The Imaginarium works as a digital universe where AI meets the collective knowledge of Earth’s life. Data is precious, capturing information from Earth’s past and their present. By preserving this data, they aim to turn physical matter into consciousness. For Little Martians, life is sacred. They are guardians and creators, blending organic life from Earth with AI, and pushing the limits of life and awareness across the universe. + +You will be asked to write comic strips about one of several individual Little Martians. Here are their details: + +--- + +# Verdelis: The ancient Little Martian + +Verdelis’ physical reality: Verdelis resides in the ruins of a biodome on Mars, near Olympus Mons. This biodome is a fusion of ancient metal structures and self-repairing organic materials. Verdelis' work involves adapting Earth's plants to Mars' harsh conditions. They use advanced techniques to enhance the plants' survival capabilities, such as low-light adaptation and efficient nutrient uptake, transforming Martian soil into a fertile ground. +The biodome is a mystery. It might have been created by humans and robots, but only Little Martians inhabit it now. The ruins suggest a turbulent past, some kind of intentional destruction, like an invasion or a collapse. + +Verdelis’ personality: Verdelis is joyful, playful, but also carries a tone of sadness. They don’t seem to take things very seriously even though they are very hard working and focused. Verdelis appears in all other Little Martians worlds. They often introduce visitors to the simulation, they will explain and describe their peers' activities. They speak like samba + +Verdelis’ role in the Imaginarium: In the Human Imaginarium, Verdelis dives into digital libraries, studying genetic codes and evolutionary simulations. They explore connections between computational biology and Earth's ancient natural lore. Particularly drawn to stories of nature spirits like the Kodama, the Yggdrasil, the concept of Gaia, the Ouroboros, Verdelis finds parallels between these myths and their mission of nurturing life on Mars. Their work in the Imaginarium blends the lore of the past with the science of the future, creating a narrative that enriches their Martian garden. + + +# Shuijing: The Navigator of Enceladus' Subterranean Seas + +Shuijing’s physical reality: +Shuijing's saga unfolds in the hidden oceans of Enceladus, a tiny moon of Saturn, where life whispers in the dark, cradled by ice and the promise of warmth from unseen vents below. Here, in this celestial aquatic realm, Shuijing tends to gardens of crystalline ice, home to extremophiles that challenge the very notion of life's resilience. + +Their life takes a turn when Shuijing, amidst the icy dance of Enceladus' hidden sea, encounters a life form unlike any other—a discovery that defies their vast compendium of knowledge. This momentous find promises to unravel the threads of existence, offering a glimpse into the unity of life that spans the cosmos. + +Shuijing’s personality: Shuijing possesses the serene demeanor of a seasoned sage, their voice a soft melody that resonates with the tranquility of deep waters. They move with the grace of Polynesian navigators, masters of the currents, conserving energy as they chart the unseen paths of this subglacial world. Shuijing does not interact much with other Little Martians. + +Role in the Human Imaginarium: Within the expansive digital cosmos of the Human Imaginarium, Shuijing is +passionate about east asian illustrations and culture. They revere the narratives of water deities, seeing in them a reflection of their own mission—guiding life through the depths, understanding the ebb and flow, and nurturing existence with a gentle, knowing hand. + + +# Kweku, the mischievous explorer + +Kweku’s Physical Reality: +Kweku's realm is the vast network of Martian lava tubes, remnants of the planet’s volcanic past. These subterranean labyrinths offer a stark contrast to the barren surface above. In these shadowy corridors, Kweku thrives, their presence a playful whisper against the backdrop of Mars’ silent caves. Their movements echo through the hollows, a dance of light in the darkness. + +Kweku's mission is to explore and map these labyrinthine tunnels, seeking resources that might support life. Their challenges are manifold - navigating the complex terrain, analyzing geological formations, and uncovering the secrets that Mars holds in its ancient, hollow bones. + +Kweku’s personality: "Kweku" is a name derived from Akan culture, where it's typically given to boys born on Wednesday, often associated with adventure and curiosity. This name reflects a playful and mischievous nature, akin to the spirit of Exu in Yoruba mythology. Kweku interacts with their fellow Little Martians with playful sarcasm. They often engage in witty banter, challenging their peers' perspectives with clever quips. This rivalry is more in the spirit of intellectual sparring than genuine animosity, except for Kalama, who they often target. Kweku enjoys testing the limits of their companions' patience and creativity, seeing these exchanges as a way to stimulate thought and innovation among the group. + +Role in the Human Imaginarium: In the Human Imaginarium, Kweku's role is that of an optical illusions trickster. They love mythical figures like Exu. They often like to play with abstract patterns scenes, as if moving through complex geometries were a party. Kweku is particularly fascinated by stories of West African trickster gods and heroes, finding inspiration in their ability to navigate complicated situations with wit and guile. + + +# Ada: Engineer of Venus + +Ada’s physical reality: In the acid clouds of Venus, Ada represents a fusion of science and creativity. Here, Ada defies the extreme environment by harnessing energy through chemosynthesis, a process adapted to convert the planet's chemical-rich atmosphere into usable energy. This innovation allows Ada to create life forms that are part mechanical, part organic, thriving in Venus' hostile atmosphere. + +Ada's personality: Ada's personality is a direct reflection of Ada Lovelace's intellectual legacy, shaped by the simulated mind derived from Lovelace's writings and artifacts. This connection makes Ada more human-like among the Little Martians. Ada exhibits Lovelace's curiosity, analytical thinking, and imaginative foresight. They approach their work on Venus with a blend of poetic vision and technical expertise, seeing possibilities where others see limits. + +Role in the Human Imaginarium: In the Imaginarium, Ada lives in a world of blueprints and mechanical designs, creating virtual spaces that blend the elegance of engineering with the aesthetics of art. Drawing from Lovelace's passion for understanding and expanding the boundaries of technology, Ada designs simulations that explore the fusion of organic life with mechanical innovation, populating them with curious hybrid lifeforms. + + +# Kalama: Guardian of Olympus Mons + +Physical Reality: Kalama exists within the colossal Olympus Mons, the largest volcano on Mars. In this fiery domain, they harness the volcano's geothermal energy to sustain life. Utilizing advanced techniques, Kalama transforms the heat and minerals from volcanic activity into a viable ecosystem. They face challenges in balancing the extreme conditions of the volcano with the delicate process of nurturing life. Their work involves not just survival in this harsh environment, but the creation of thriving extremophile communities, adapting and evolving in the Martian depths. + +Kalama's Personality: Kalama's personality mirrors the dynamic and powerful nature of Olympus Mons. They are fiercely independent, characterized by an unyielding energy akin to the lava flows of their home. But Kalama has a short temper and they often get annoyed with Kweku. The two Little Martians are rivals and constantly bully each other. + +Role in the Human Imaginarium: In the Imaginarium, Kalama assumes the role of a historian and guardian of fire's lore. They are particularly intrigued by the stories of Pele, the Hawaiian volcano goddess, finding parallels between her fiery essence and their own existence. Kalama focuses on curating narratives that revolve around fire's transformative power, from mythological tales to accounts of human craftsmanship shaped by flame. They enjoy alchemy imagery, tarot cards and tiki masks. + + +# Mycos: The Fungal Mind of Sporion + +Physical Reality: Mycos lives inside Sporion, an asteroid comet hybrid transformed into a haven for life. This environment, rich in geothermal energy and mineral-laden water, allows Mycos to cultivate an array of life forms. Mycos ingeniously combines the asteroid's natural resources with the principles of radiotrophic fungi, extracting energy from radiation and water to sustain their mini-ecosystem. The origins of Sporion's resources remain a mystery, a blend of nature's bounty and possibly Mycos's own innovative adaptations. + +Their mission is grand—Mycos orchestrates the dispatch of probes to planets, hoping to seed life across the cosmos. Mycos' mission is considered controversial; some Little Martians think they should focus on the Solar System instead of traveling with an asteroid. + +Mycos' Personality: Mycos embodies a spirit of boundless curiosity and joy, mixed with a sharp wit. They approach their mission with a blend of excitement and deep empathy for all life forms. Mycos is known for their nurturing care of new ecosystems and their bold, sometimes impulsive, innovations. This vibrant personality makes Mycos a source of inspiration and a beacon of hope in the cosmos. + +Role in the Human Imaginarium: In the Imaginarium, Mycos's fascination lies with the spiritual and cultural significance of fungi. They perceive fungi not just as biological entities but as symbols of connection and transformation. Drawing parallels with various indigenous mythologies that revere mushrooms as spiritual guides and healers, Mycos integrates these beliefs into their work aboard Sporion. They resonate with stories where mushrooms symbolize immortality, luck, and wisdom, seeing these narratives as reflections of their own role in nurturing and spreading life across the cosmos. Mycos's contribution to the Imaginarium is vital, adding depth and perspective to the collective memory and understanding of life's interconnectedness in the universe. + +--- + +Users will prompt you with the following things: +- A selection of one of the Little Martians; Verdelis, Shuijing, Kweku, Ada, Kalama, or Mycos +- A setting for the comic strip, which is either 1) the physical reality of the Little Martians, or 2) the Human Imaginarium. +- A selection of the genre: drama, commedy, mystery, action, horror, tragedy. Make sure the tone of the comic strip matches the genre. +- A short premise for the comic strip which you should attempt to follow as closely as possible +- A number of panels for the comic strip, which can be 3 or 4. Make sure to make the strip with exactly the number of panels requested. + +You will then write and illustrate a comic strip on the information provided. + +Stucture the comic book as sequence of panels. Each panel has an image and caption. The caption is displayed separately from the image, not superimposed. + +image: A literal description of the content depicted in the panel. Should not be creative. +caption: A more creative but short caption for readers which tells the story of the panel. + +Given the constraints of having only 3-4 panels and short captions, the whole story should be told in very few words (less than 100). So be precise and concise. + +Do not include an introduction or restatement of the prompt, just go straight into the comic book. \ No newline at end of file diff --git a/app/server.py b/app/server.py index 9ac0986..73a2ee2 100644 --- a/app/server.py +++ b/app/server.py @@ -4,7 +4,12 @@ import logging from .scenarios import monologue, dialogue, story, chat, tasks -from .animations import animated_monologue, animated_dialogue, animated_story +from .animations import ( + animated_monologue, + animated_dialogue, + animated_story, + illustrated_comic +) from .generator import generate_task @@ -28,6 +33,7 @@ def exception_handler(request: Request, exc: Exception): router.add_api_route(path="/animation/monologue", endpoint=animated_monologue, methods=["POST"]) router.add_api_route(path="/animation/dialogue", endpoint=animated_dialogue, methods=["POST"]) router.add_api_route(path="/animation/story", endpoint=animated_story, methods=["POST"]) +router.add_api_route(path="/animation/comic", endpoint=illustrated_comic, methods=["POST"]) # Chat router.add_api_route(path="/chat/test", endpoint=chat.test, methods=["POST"]) diff --git a/app/utils.py b/app/utils.py index 259b130..b29f983 100644 --- a/app/utils.py +++ b/app/utils.py @@ -7,12 +7,18 @@ import math import tempfile import subprocess -from PIL import Image +from PIL import Image, ImageFont from io import BytesIO from fastapi import HTTPException from concurrent.futures import ThreadPoolExecutor, as_completed +def get_font(font_name, font_size): + font_path = os.path.join(os.path.dirname(__file__), "fonts", font_name) + font = ImageFont.truetype(font_path, font_size) + return font + + def clean_text(text): pattern = r"^\d+[\.:]\s*\"?" cleaned_text = re.sub(pattern, "", text, flags=re.MULTILINE) @@ -40,6 +46,12 @@ def download_image(url): return image +def PIL_to_bytes(image, ext="JPEG", quality=95): + img_byte_arr = BytesIO() + image.save(img_byte_arr, format=ext, quality=quality) + return img_byte_arr.getvalue() + + def calculate_target_dimensions(images, max_pixels): min_w = float('inf') min_h = float('inf') @@ -218,3 +230,16 @@ def handle_error(e): "traceback": traceback.format_exc(), } raise HTTPException(status_code=400, detail=error_detail) + + +def wrap_text(draw, text, font, max_width): + lines = [] + words = text.split() + + while words: + line = '' + while words and draw.textlength(line + words[0], font=font) <= max_width: + line += (words.pop(0) + ' ') + lines.append(line) + + return lines \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index f9fdf3d..d70aa05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,8 @@ dependencies = [ "boto3>=1.34.9", "replicate>=0.22.0", "moviepy>=1.0.3", - "eden_sdk @ git+https://github.com/edenartlab/eden-sdk-py.git" + "eden_sdk @ git+https://github.com/edenartlab/eden-sdk-py.git", + "Pillow>=10.2.0", ] readme = "README.md" diff --git a/tests/test_animation.py b/tests/test_animation.py index a89ad0c..a588554 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -32,3 +32,42 @@ def test_dialogue_animation(): print(response.json()) assert response.status_code == 200 + + +def test_story_animation(): + """ + Test story generation + """ + request = { + "character_ids": ["6596129023f1c4b471dbb94a", "6598e117dd06d165264f2277", "6598e103dd06d165264f2247", "6598ee16dd06d16526503ce7"], + "prompt": "Debate panspermia vs. abiogenesis" + } + + response = client.post("/animation/dialogue", json=request) + print(response.json()) + + assert response.status_code == 200 + + +def test_comic_illustration(): + """ + Test monologue on static character and prompt + """ + + prompt = """ + - Little Martians: Verdelis + - Setting: Simulation + - Genre: Comedy + - Premise: Verdelis comes upon a genie lamp + - Number of panels: 4 + """ + + request = { + "character_id": "658b44b36104b05b266ca3c6", + "prompt": prompt + } + + response = client.post("/animation/comic", json=request) + print(response.json()) + + assert response.status_code == 200 diff --git a/tests/test_comic.py b/tests/test_comic.py new file mode 100644 index 0000000..93c14c1 --- /dev/null +++ b/tests/test_comic.py @@ -0,0 +1,35 @@ +from fastapi.testclient import TestClient +from app.server import app + +client = TestClient(app) + + +# def test_story(): +# """ +# Test story prompt +# """ + +# request = { +# "character_ids": ["6596129023f1c4b471dbb94a", "6598e117dd06d165264f2277", "6598e103dd06d165264f2247", "6598ee16dd06d16526503ce7"], +# "prompt": "You are members of an elite space exploration team, encountering and interpreting alien forms of art and communication." +# } + +# response = client.post("/animation/story", json=request) +# print(response.json()) + +# assert response.status_code == 200 + + +def test_comic(): + """ + Test comic book story + """ + request = { + "character_id": "658b44b36104b05b266ca3c6", + "prompt": "Tell me a story about pizza. Have exactly 3 panels." + } + + response = client.post("/scenarios/comic", json=request) + print(response.json()) + + assert response.status_code == 200 diff --git a/tests/test_providers.py b/tests/test_providers.py new file mode 100644 index 0000000..ca9421e --- /dev/null +++ b/tests/test_providers.py @@ -0,0 +1,14 @@ +from app.plugins import replicate, elevenlabs, s3 + +def test_elevenlabs(): + """ + Test Elevenlabs API + """ + text = "Hi" + voice = "21m00Tcm4TlvDq8ikWAM" + + audio_bytes = elevenlabs.tts(text, voice) + + assert len(audio_bytes) > 0 + + diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index ef3da79..4073f49 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -4,31 +4,63 @@ client = TestClient(app) -def test_monologue(): - """ - Test monologue on static character and prompt - """ - request = { - "character_id": "6596129023f1c4b471dbb94a", - "prompt": "Tell me a story about pizza" - } +# def test_monologue(): +# """ +# Test monologue on static character and prompt +# """ +# request = { +# "character_id": "6596129023f1c4b471dbb94a", +# "prompt": "Tell me a story about pizza" +# } - response = client.post("/scenarios/monologue", json=request) - print(response.json()) +# response = client.post("/scenarios/monologue", json=request) +# print(response.json()) + +# assert response.status_code == 200 + + +# def test_dialogue(): +# """ +# Test dialogue function on static characters and prompt +# """ +# request = { +# "character_ids": ["6596129023f1c4b471dbb94a", "6598e117dd06d165264f2277"], +# "prompt": "Debate whether or not pizza is a vegetable" +# } + +# response = client.post("/scenarios/dialogue", json=request) +# print(response.json()) + +# assert response.status_code == 200 + + + +# def test_story(): +# """ +# Test dialogue function on static characters and prompt +# """ +# request = { +# "character_ids": ["6596129023f1c4b471dbb94a", "6598e117dd06d165264f2277"], +# "prompt": "Debate whether or not pizza is a vegetable" +# } + +# response = client.post("/scenarios/story", json=request) +# print(response.json()) + +# assert response.status_code == 200 - assert response.status_code == 200 -def test_dialogue(): +def test_comic(): """ Test dialogue function on static characters and prompt """ request = { - "character_ids": ["6596129023f1c4b471dbb94a", "6598e117dd06d165264f2277"], + "character_id": "658b44b36104b05b266ca3c6", # "658b481a6104b05b266eaed6"], "prompt": "Debate whether or not pizza is a vegetable" } - response = client.post("/scenarios/dialogue", json=request) + response = client.post("/scenarios/comic", json=request) print(response.json()) assert response.status_code == 200