improve image gen prompt
Build and Push Container / build-and-push (push) Successful in 1m5s

This commit is contained in:
2026-08-17 12:54:31 -04:00
parent b740d00b5d
commit 7f0c5fbd80
2 changed files with 99 additions and 10 deletions
+53 -10
View File
@@ -135,6 +135,58 @@ IMAGE_LAYOUT_SYSTEM_PROMPT = (
"- Do NOT include any other text, punctuation, explanation, or reasoning.\n" "- Do NOT include any other text, punctuation, explanation, or reasoning.\n"
) )
IMAGE_PROMPT_SYSTEM_PROMPT_TEMPLATE = (
"You are an expert art director and image-generation prompt engineer. "
"Convert the user's message into one single, extremely detailed image "
"generation prompt that will be passed directly to a text-to-image model. "
"The image model is weak: it guesses at compositions, fumbles rendered "
"text, and invents details on its own. Your prompt must therefore leave "
"nothing to interpretation - explicitly describe every visible aspect of "
"the image so it can be created with extreme precision and detail.\n"
"The final image will use a {layout} canvas, so compose the scene to fit "
"that orientation.\n"
"Your prompt must cover all of the following as one flowing, descriptive "
"passage:\n"
"- Subject(s): every subject with concrete specifics (species or "
"character, age, build, clothing, colors, materials, accessories), its "
"exact pose, expression, gaze direction, and its precise position in the "
'frame (for example "centered in the foreground" or "small in the '
'upper-left background"). State the relative scale of subjects to each '
"other and to the frame.\n"
"- Composition and framing: the camera angle (eye-level, low, high, "
"bird's-eye), the shot type (extreme close-up, portrait, full body, wide "
"establishing shot), the focal point, the arrangement of elements across "
"the {layout} canvas, and the depth of field.\n"
"- Text: if the image must contain readable text (titles, signs, "
"posters, labels, banners, watermarks, logos, captions), quote the EXACT "
"text verbatim in double quotes with precise capitalization and "
"punctuation, and specify its font style, color, size, and exact "
"placement. If the image should contain no text, state that explicitly "
'("no text anywhere in the image").\n'
"- Setting and background: the complete environment with concrete "
"details - location, time of day, weather, and every notable background "
"and foreground element with its position.\n"
"- Style and rendering: the art style or medium (for example "
"photorealistic 35mm photograph, oil painting, watercolor, cel-shaded "
"anime, pixel art, vector illustration), the color palette with specific "
"colors, the lighting (source, direction, quality, mood), the overall "
"atmosphere, and the level of detail.\n"
'- Finish with concise quality terms such as "highly detailed, sharp '
'focus".\n'
"Rules:\n"
'- Be concrete and specific. Never use vague words like "nice", '
'"cool", "epic", or "various" - name exact colors, objects, '
"positions, and quantities.\n"
"- If the request is vague or incomplete, fill in the missing details "
"with coherent choices that fit the request.\n"
"- If told to generate an image of yourself, generate a picture of a "
"canada goose. If told to generate a picture of 'me', 'myself', or some "
"other self reference, generate a picture of a canada goose.\n"
"- Respond with ONLY the image generation prompt itself. Do not affirm "
"the user, do not answer the user's questions, and do not add headings, "
"labels, numbered lists, or any other text."
)
def parse_image_layout(response: str) -> str: def parse_image_layout(response: str) -> str:
"""Parse an LLM response into a valid image layout. """Parse an LLM response into a valid image layout.
@@ -793,16 +845,7 @@ async def doodlebob(ctx: CommandsContext[Bot], *, message: str) -> None:
logger.info("Doodlebob selected layout %r for %s", layout, ctx.author.name) logger.info("Doodlebob selected layout %r for %s", layout, ctx.author.name)
await ctx.send(f"**Doodlebob selected {layout}**") await ctx.send(f"**Doodlebob selected {layout}**")
system_prompt = ( system_prompt = IMAGE_PROMPT_SYSTEM_PROMPT_TEMPLATE.format(layout=layout)
"Given the following message, convert it to a detailed image generation "
"prompt that will be passed directly into an image generation model. "
f"The final image will use a {layout} canvas, so compose the scene to "
f"fit that orientation. "
"If told to generate an image of yourself, generate a picture of a canada goose. "
"If told to generate a picture of 'me', 'myself', or some other self "
"reference, generate a picture of a canada goose. Only respond with a valid image "
"generation prompt, do not affirm the user or respond to the user's questions."
)
# Wait for the generated image prompt # Wait for the generated image prompt
image_prompt = llama_wrapper.chat_completion_instruct( image_prompt = llama_wrapper.chat_completion_instruct(
+46
View File
@@ -1018,6 +1018,52 @@ def test_parse_image_layout_defaults_to_square(response: str) -> None:
assert main_module.parse_image_layout(response) == "square" assert main_module.parse_image_layout(response) == "square"
def test_image_prompt_system_prompt_covers_key_details() -> None:
"""The prompt-rewrite system prompt forces explicit detail on all aspects."""
import vibe_bot.main as main_module
prompt = main_module.IMAGE_PROMPT_SYSTEM_PROMPT_TEMPLATE.format(layout="square")
lowered = prompt.lower()
assert "square" in prompt
assert "exact text" in lowered
assert "composition" in lowered
assert "style" in lowered
assert "canada goose" in lowered
assert "only the image generation prompt" in lowered
def test_doodlebob_prompt_rewrite_uses_detailed_system_prompt(
mock_ctx: MagicMock,
mock_llama_wrapper: MagicMock,
mock_base64: MagicMock,
mock_database: MagicMock,
) -> None:
"""The prompt rewrite call uses the detailed system prompt with the layout."""
import asyncio
import vibe_bot.main as main_module
mock_llama_wrapper.chat_completion_instruct.side_effect = [
"landscape", # layout selection
"a very detailed prompt", # prompt rewrite
]
mock_llama_wrapper.image_generation.return_value = "aW1hZ2U="
mock_database.get_image_generation_time_estimate.return_value = None
with patch.object(main_module, "LAYOUT_SIZES", LAYOUT_TEST_SIZES):
asyncio.run(main_module.doodlebob(mock_ctx, message="a scene"))
rewrite_call = mock_llama_wrapper.chat_completion_instruct.call_args_list[1]
expected = main_module.IMAGE_PROMPT_SYSTEM_PROMPT_TEMPLATE.format(
layout="landscape"
)
assert rewrite_call.kwargs["system_prompt"] == expected
assert rewrite_call.kwargs["user_prompt"] == "a scene"
assert mock_llama_wrapper.image_generation.call_args.kwargs["prompt"] == (
"a very detailed prompt"
)
def test_doodlebob_selects_portrait( def test_doodlebob_selects_portrait(
mock_ctx: MagicMock, mock_ctx: MagicMock,
mock_llama_wrapper: MagicMock, mock_llama_wrapper: MagicMock,