rupkotha / core /prompts.py
Deb
rupkotha_1st_commit
f655146
Raw
History Blame Contribute Delete
9.14 kB
# core/prompts.py
"""All prompt templates (EN + BN, per style).
Stories must read like a grandmother's bedtime tale: warm, simple, rhythmic,
120–150 words, gentle sleepy ending, never scary, never a cliffhanger.
Templates use {instruction}, {name_line}, {words}, {style_rule} placeholders.
Bengali prompts are written natively in বাংলা — never "translate to Bengali".
NOTE: This is a Step 1 skeleton. Real templates are filled in at Build Order
steps 2 (EN) and 3 (BN).
"""
# Story-style keys exposed to the UI. EN and BN have separate style sets.
STYLES: dict[str, dict[str, str]] = {
"en": {
"Adventure 🌟": "a gentle adventure where nothing truly scary happens",
"Funny 😄": "a silly, giggly tale full of light-hearted fun",
"Magical ✨": "a soft, wondrous tale with a touch of quiet magic",
},
"bn": {
"রূপকথা": "একটি কোমল রূপকথা যেখানে ভয় পাওয়ার কিছু নেই",
"মজার": "একটি হাসিখুশি, মজার গল্প",
"জাদু": "একটি কোমল, জাদুমাখা গল্প",
},
}
DEFAULT_WORDS = "120 to 150"
# Hard cap on images woven into one story (CLAUDE.md §1: 1–4 images).
MAX_IMAGES = 4
def _images_rule(language: str, num_images: int) -> str:
"""Count-aware instruction for weaving 0–4 pictures into ONE story cast."""
n = max(0, min(num_images, MAX_IMAGES))
if language == "bn":
if n == 0:
return "এবার কোনো ছবি নেই — তুমি নিজেই একটা আরামদায়ক ছোট্ট দৃশ্য কল্পনা করে নাও।"
if n == 1:
return "ছবিটি ভালো করে দেখো আর তাতে যা সত্যিই আছে — শিশুর নিজের আঁকা ও খেলনা — গল্পে বুনে দাও।"
return (
f"{n}টি ছবি আছে। সবগুলোকে একটিই গল্পে বুনে দাও — প্রতিটি ছবি যেন একই "
"গল্পের এক একটি চরিত্র, বন্ধু বা জায়গা হয়, সবাই একসাথে মিলেমিশে।"
)
if n == 0:
return "There is no picture this time — gently imagine a cosy little scene yourself."
if n == 1:
return "Use what is actually in the picture — the child's own drawing or toy."
return (
f"There are {n} pictures. Weave them ALL into a SINGLE story — let each "
"picture become a character, friend, or place that meets in the same gentle tale."
)
# Gemma (Stacks B/C) overshoots word counts and writes lush prose; MiniCPM (Stack A)
# runs terse. Nudge the cap per stack family. See CLAUDE.md §8.
_WORDS_BY_STACK = {"A": "130 to 160", "B": "110 to 140", "C": "110 to 140"}
# Bengali: a shorter target than English. A long Bengali span pushes the model
# past its reliable range and invites invented words / script drift — keep it tight.
_BN_WORDS = "80 to 110"
# A tiny clean রূপকথা anchor (few-shot): locks Bengali script, register, and the
# soft sleepy rhythm so the model imitates good বাংলা instead of drifting.
_BN_EXAMPLE = (
"যেমন: “এক যে ছিল ছোট্ট খরগোশ, থাকত পুকুরপাড়ে। সারাদিন মাঠে খেলত, "
"সন্ধ্যা হলে মায়ের কোলে ঘুমিয়ে পড়ত। শুভরাত্রি।”"
)
# Lever C — two-pass. Pass 1 asks the model to DESCRIBE the image(s) in English
# (its vision strength); pass 2 narrates from that text so the model spends all
# its capacity on Bengali prose, not on perceiving the image at the same time.
SCENE_SENTINEL = "[[SCENE_DESCRIPTION]]"
DESCRIBE_PROMPT_EN = (
"Look carefully at the picture(s) a child has shared. In 3–5 simple English "
"sentences, describe ONLY what you actually see: the objects, their colours, "
"and the overall mood. Do not tell a story — just describe what is in the picture(s)."
)
_TEMPLATE_EN = """You are a warm, loving grandmother telling a bedtime story to a small child.
Look at the picture(s) the child has shared and weave what you see into {style_rule}.
{name_line}The child asked: "{instruction}"
{scene}
Rules:
- Write {words} words. Simple words a young child knows. Gentle, rhythmic, soothing.
- {images_rule}
- Never anything scary, sad, or a cliffhanger.
- End with everyone safe, cosy, and settling down to sleep.
Tell the story now, in English:"""
_TEMPLATE_BN = """তুমি একজন স্নেহময়ী ঠাকুমা, ছোট্ট এক শিশুকে ঘুমপাড়ানি গল্প বলছ।
শিশুটি যে ছবি(গুলি) দেখিয়েছে তা দেখো এবং সেটিকে গল্পে বুনে দাও — {style_rule}।
{name_line}শিশুটি বলেছে: "{instruction}"
{scene}
নিয়ম:
- শুধু বিশুদ্ধ বাংলা অক্ষরে লেখো — কোনো ইংরেজি বা রোমান হরফ ব্যবহার করবে না।
- কেবল চেনা, সঠিক বাংলা শব্দ ব্যবহার করো — কোনো বানানো বা অর্থহীন শব্দ নয়।
- {words}টি শব্দে লেখো। ছোট শিশুর চেনা সহজ শব্দ। কোমল, ছন্দময়, আদুরে।
- {images_rule}
- পুকুর, মাঠ, জোনাকি, চাঁদমামার মতো চেনা বাংলা ছবি ব্যবহার করো।
- কখনও ভয়ের, দুঃখের বা অসমাপ্ত কিছু নয়।
- সবাই নিরাপদে, আরামে, ঘুমিয়ে পড়ার মধ্য দিয়ে গল্প শেষ করো।
{example}
এবার বাংলায় গল্পটি বলো:"""
def _scene_block(language: str, scene_description: str) -> str:
"""Two-pass: a grounding block holding the (English) scene description for
pass 2. Empty in single-pass mode."""
if not scene_description:
return ""
if language == "bn":
return f"\nছবিতে যা আছে তার বর্ণনা: {scene_description}\n"
return f"\nWhat is in the picture: {scene_description}\n"
def _scene_rule(language: str) -> str:
"""Two-pass replacement for the images_rule: weave the described scene in."""
if language == "bn":
return "উপরের বর্ণনায় থাকা জিনিসগুলো গল্পে বুনে দাও।"
return "Weave the things in the description above into the story."
def build_story_prompt(
instruction: str,
language: str,
style: str,
child_name: str = "",
stack_key: str = "A",
num_images: int = 1,
scene_description: str = "",
) -> str:
"""Build the full story prompt for the vision model. language: 'en' | 'bn'.
num_images (0–4) drives how the prompt asks the model to weave the pictures
into one shared story cast.
scene_description: two-pass (Lever C). When set, this is the pass-2 prompt —
it injects the (English) scene description as text and the model narrates from
it with NO image attached. Pass a sentinel (SCENE_SENTINEL) here and have the
Modal layer substitute the real description after the describe pass."""
lang = language if language in ("en", "bn") else "en"
styles = STYLES[lang]
style_rule = styles.get(style) or next(iter(styles.values()))
words = _WORDS_BY_STACK.get(stack_key, DEFAULT_WORDS)
# In two-pass mode the story is grounded in the description, not the raw image.
images_rule = _scene_rule(lang) if scene_description else _images_rule(lang, num_images)
scene = _scene_block(lang, scene_description)
if lang == "bn":
name_line = f"শিশুটির নাম {child_name}; গল্পে তার নাম বুনে দাও।\n" if child_name else ""
instr = instruction.strip() or "আমাকে একটা গল্প বলো"
return _TEMPLATE_BN.format(
style_rule=style_rule, name_line=name_line, instruction=instr,
words=_BN_WORDS, images_rule=images_rule, example=_BN_EXAMPLE, scene=scene,
)
name_line = f"The child's name is {child_name}; weave their name in warmly.\n" if child_name else ""
instr = instruction.strip() or "tell me a bedtime story"
return _TEMPLATE_EN.format(
style_rule=style_rule, name_line=name_line, instruction=instr,
words=words, images_rule=images_rule, scene=scene,
)