Release Candidate: Production Release with Multi-Tenant & Onboarding Enhancements

This commit is contained in:
ajaysi
2026-02-28 20:06:26 +05:30
parent 08a1f4a1d8
commit 4828274cbf
162 changed files with 19489 additions and 4300 deletions

View File

@@ -145,20 +145,45 @@ Write ONLY the premise sentence(s). Do not write anything else.
"reasoning",
],
},
"minItems": 1,
"maxItems": 1,
}
},
"required": ["options"],
}
def _build_idea_enhance_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"suggestions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"idea": {"type": "string"},
"whats_missing": {"type": "string"},
"why_choose": {"type": "string"},
},
"required": ["idea", "whats_missing", "why_choose"],
},
"minItems": 3,
"maxItems": 3,
}
},
"required": ["options"],
"required": ["suggestions"],
}
def generate_story_setup_options(
self,
*,
story_idea: str,
story_mode: str | None,
story_template: str | None,
brand_context: Dict[str, Any] | None,
user_id: str,
) -> List[Dict[str, Any]]:
"""Generate 3 story setup options from a user's story idea."""
"""Generate a single story setup option from a user's story idea."""
suggested_writing_styles = ['Formal', 'Casual', 'Poetic', 'Humorous', 'Academic', 'Journalistic', 'Narrative']
suggested_story_tones = ['Dark', 'Uplifting', 'Suspenseful', 'Whimsical', 'Melancholic', 'Mysterious', 'Romantic', 'Adventurous']
@@ -167,12 +192,59 @@ Write ONLY the premise sentence(s). Do not write anything else.
suggested_content_ratings = ['G', 'PG', 'PG-13', 'R']
suggested_ending_preferences = ['Happy', 'Tragic', 'Cliffhanger', 'Twist', 'Open-ended', 'Bittersweet']
mode_label = None
if story_mode == "marketing":
mode_label = "Non-fiction marketing story (brand or product campaign)"
elif story_mode == "pure":
mode_label = "Fiction story"
template_label = None
if story_template == "product_story":
template_label = "Product Story"
elif story_template == "brand_manifesto":
template_label = "Brand Manifesto"
elif story_template == "founder_story":
template_label = "Founder Story"
elif story_template == "customer_story":
template_label = "Customer Story"
elif story_template == "short_fiction":
template_label = "Short Fiction"
elif story_template == "long_fiction":
template_label = "Long Fiction"
elif story_template == "anime_fiction":
template_label = "Anime Fiction"
elif story_template == "experimental_fiction":
template_label = "Experimental Fiction"
brand_name = None
writing_tone = None
audience_description = None
if isinstance(brand_context, dict):
brand_name = brand_context.get("brand_name")
writing_tone = brand_context.get("writing_tone")
target_audience = brand_context.get("target_audience")
if isinstance(target_audience, dict):
audience_description = target_audience.get("description") or target_audience.get("summary")
elif isinstance(target_audience, str):
audience_description = target_audience
setup_prompt = f"""\
You are an expert story writer and creative writing assistant. A user has provided the following story idea or information:
You are an expert story writer and creative writing assistant.
{"This is a " + mode_label + "." if mode_label else ""}
{("The user selected the template: " + template_label + ".") if template_label else ""}
The story should stay consistent with the brand and audience context below when relevant:
- Brand name or site: {brand_name or "Not specified"}
- Headline/overall writing tone: {writing_tone or "Not specified"}
- Audience description: {audience_description or "Not specified"}
The user has provided the following story idea or information:
{story_idea}
Based on this story idea, generate exactly 3 different, well-thought-out story setup options. Each option should be CREATIVE, PERSONALIZED, and perfectly tailored to the user's specific story idea.
Based on this story idea, generate exactly 1 well-thought-out story setup option. The setup should be CREATIVE, PERSONALIZED, and perfectly tailored to the user's specific story idea.
**CRITICAL - Creative Freedom:**
- You have COMPLETE FREEDOM to craft personalized values that best fit the user's story idea
@@ -183,7 +255,7 @@ Based on this story idea, generate exactly 3 different, well-thought-out story s
- Narrative POV: "Second Person (You)" or "Omniscient Narrator as Guide" (not just standard options)
- The goal is to create the PERFECT setup for THIS specific story, not to fit into generic categories
Each option should:
The setup should:
1. Have a unique and creative persona that fits the story idea perfectly
2. Define a compelling story setting that brings the idea to life
3. Describe interesting and engaging characters
@@ -212,23 +284,23 @@ Each option should:
**Remember:** These are ONLY suggestions. If a custom value better serves the story idea, CREATE IT!
Return exactly 3 options as a JSON array. Each option must include a "premise" field with the story premise.
Return exactly 1 option as a JSON array with a single object in "options". The object must include a "premise" field with the story premise.
"""
setup_schema = self._build_setup_schema()
try:
logger.info(f"[StoryWriter] Generating story setup options for user {user_id}")
logger.info(f"[StoryWriter] Generating story setup option for user {user_id}")
response = self.load_json_response(
llm_text_gen(prompt=setup_prompt, json_struct=setup_schema, user_id=user_id)
)
options = response.get("options", [])
if len(options) != 3:
logger.warning(f"[StoryWriter] Expected 3 options but got {len(options)}, correcting count")
if len(options) < 3:
raise ValueError(f"Expected 3 options but got {len(options)}")
options = options[:3]
if len(options) != 1:
logger.warning(f"[StoryWriter] Expected 1 option but got {len(options)}, correcting count")
if len(options) < 1:
raise ValueError(f"Expected 1 option but got {len(options)}")
options = options[:1]
for idx, option in enumerate(options):
if not option.get("premise") or not option.get("premise", "").strip():
@@ -262,7 +334,7 @@ Return exactly 3 options as a JSON array. Each option must include a "premise" f
premise += "."
option["premise"] = premise
logger.info(f"[StoryWriter] Generated {len(options)} story setup options with premises for user {user_id}")
logger.info(f"[StoryWriter] Generated {len(options)} story setup option(s) with premise for user {user_id}")
return options
except HTTPException:
raise
@@ -273,3 +345,119 @@ Return exactly 3 options as a JSON array. Each option must include a "premise" f
logger.error(f"[StoryWriter] Error generating story setup options: {exc}")
raise RuntimeError(f"Failed to generate story setup options: {exc}") from exc
def enhance_story_idea(
self,
*,
story_idea: str,
story_mode: str | None,
story_template: str | None,
brand_context: Dict[str, Any] | None,
user_id: str,
fiction_variant: str | None = None,
narrative_energy: str | None = None,
) -> List[Dict[str, Any]]:
mode_label = None
if story_mode == "marketing":
mode_label = "Non-fiction marketing story (brand or product campaign)"
elif story_mode == "pure":
mode_label = "Fiction story"
template_label = None
if story_template == "product_story":
template_label = "Product Story"
elif story_template == "brand_manifesto":
template_label = "Brand Manifesto"
elif story_template == "founder_story":
template_label = "Founder Story"
elif story_template == "customer_story":
template_label = "Customer Story"
elif story_template == "short_fiction":
template_label = "Short Fiction"
elif story_template == "long_fiction":
template_label = "Long Fiction"
elif story_template == "anime_fiction":
template_label = "Anime Fiction"
elif story_template == "experimental_fiction":
template_label = "Experimental Fiction"
brand_name = None
writing_tone = None
audience_description = None
if isinstance(brand_context, dict):
brand_name = brand_context.get("brand_name")
writing_tone = brand_context.get("writing_tone")
target_audience = brand_context.get("target_audience")
if isinstance(target_audience, dict):
audience_description = target_audience.get("description") or target_audience.get("summary")
elif isinstance(target_audience, str):
audience_description = target_audience
fiction_focus_line = ""
if fiction_variant:
fiction_focus_line = f'Treat the story as "{fiction_variant}" and lean into that creative focus.'
energy_line = ""
if narrative_energy:
energy_line = f'Target narrative energy: {narrative_energy}.'
enhance_prompt = f"""You are a creative writing coach helping a user refine and expand a story idea.
{"This is a " + mode_label + "." if mode_label else ""}
{("The user selected the template: " + template_label + ".") if template_label else ""}
{fiction_focus_line}
{energy_line}
When relevant, keep the idea aligned with this brand and audience context:
- Brand name or site: {brand_name or "Not specified"}
- Headline/overall writing tone: {writing_tone or "Not specified"}
- Audience description: {audience_description or "Not specified"}
The user has written the following story idea or concept:
{story_idea}
Your task is to propose exactly 3 alternative enhanced story idea options.
Each option must:
- Preserve the user's core premise and intent.
- Make the premise clearer and more compelling.
- Surface the central conflict or tension.
- Clarify the main characters and their goals.
- Strengthen the setting and stakes.
- Stay at the "idea" level, not a full outline or beat-by-beat breakdown.
For each option, return three fields:
- "idea": 2-4 sentences describing the improved story idea, suitable for a single textarea input.
- "whats_missing": 2-4 sentences explaining what important details are missing or underspecified in the current brief. Focus on gaps such as: protagonist details, antagonist or opposing force, stakes, setting and time period, audience/age group, subgenre or type of fiction (for example, anime vs grounded sci-fi), language or tone preferences, and any format constraints.
- "why_choose": 1-3 sentences explaining how this option interprets the original idea and why it might be a strong direction for the story.
Do not write a full story outline.
Do not output numbered lists or markdown formatting.
Return a single JSON object with a "suggestions" array of 3 items, where each item has the keys "idea", "whats_missing", and "why_choose"."""
schema = self._build_idea_enhance_schema()
try:
logger.info(f"[StoryWriter] Enhancing story idea with structured suggestions for user {user_id}")
response = self.load_json_response(
llm_text_gen(prompt=enhance_prompt, json_struct=schema, user_id=user_id)
)
suggestions = response.get("suggestions", [])
if len(suggestions) != 3:
logger.warning(
f"[StoryWriter] Expected 3 idea suggestions but got {len(suggestions)}, correcting count"
)
if len(suggestions) < 3:
raise ValueError(f"Expected 3 suggestions but got {len(suggestions)}")
suggestions = suggestions[:3]
return suggestions
except HTTPException:
raise
except json.JSONDecodeError as exc:
logger.error(f"[StoryWriter] Failed to parse JSON response for story idea enhancement: {exc}")
raise RuntimeError(f"Failed to parse story idea enhancement suggestions: {exc}") from exc
except Exception as exc:
logger.error(f"[StoryWriter] Error enhancing story idea: {exc}")
raise RuntimeError(f"Failed to enhance story idea: {exc}") from exc