Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -150,7 +150,7 @@ def _generate_with_openai_provider(client, model_id, prompt, max_tokens, system_
|
|
150 |
response = client.chat.completions.create(
|
151 |
model=model_id,
|
152 |
messages=messages,
|
153 |
-
temperature=0.
|
154 |
max_tokens=max_tokens
|
155 |
)
|
156 |
return response.choices[0].message.content
|
@@ -169,7 +169,7 @@ def _generate_with_gemini_provider(client, model_id, prompt, max_tokens, system_
|
|
169 |
'HARM_CATEGORY_SEXUALLY_EXPLICIT': 'block_none',
|
170 |
'HARM_CATEGORY_DANGEROUS_CONTENT': 'block_none',
|
171 |
},
|
172 |
-
generation_config=client.types.GenerationConfig(temperature=0.
|
173 |
)
|
174 |
response = model.generate_content(full_prompt)
|
175 |
|
@@ -206,7 +206,7 @@ def _generate_with_deepseek_provider(api_key, model_id, prompt, max_tokens, syst
|
|
206 |
payload = {
|
207 |
"model": model_id,
|
208 |
"messages": messages,
|
209 |
-
"temperature": 0.
|
210 |
"max_tokens": max_tokens
|
211 |
}
|
212 |
try:
|
@@ -235,7 +235,8 @@ def _generate_with_claude_provider(client, model_id, prompt, max_tokens, system_
|
|
235 |
system=system_message if system_message else None,
|
236 |
messages=[
|
237 |
{"role": "user", "content": prompt}
|
238 |
-
]
|
|
|
239 |
)
|
240 |
content = ""
|
241 |
if message.content:
|
@@ -352,6 +353,8 @@ def render_mermaid_diagram(mermaid_code, key):
|
|
352 |
)
|
353 |
|
354 |
# ---------- Initialize Session State (Provibe Workflow) ----------
|
|
|
|
|
355 |
# Core workflow steps
|
356 |
if 'current_step' not in st.session_state:
|
357 |
st.session_state.current_step = "input_idea" # input_idea -> refine_idea -> review_idea -> generate_docs -> display_docs
|
@@ -555,8 +558,8 @@ doc_options = {
|
|
555 |
|
556 |
# ---------- UI Layout (Provibe Workflow) ----------
|
557 |
st.set_page_config(layout="wide", page_title="Provibe Prompt Tester")
|
558 |
-
st.title("🧪 Provibe Prompt Tester (with Q&A Refinement)")
|
559 |
-
st.caption("Test and refine prompts for the Provibe document generation workflow, including interactive Q&A.")
|
560 |
|
561 |
# Display API Key Errors
|
562 |
if secret_errors:
|
@@ -570,6 +573,8 @@ if not any_secret_loaded or not SUPPORTED_MODELS:
|
|
570 |
# --- Workflow Steps ---
|
571 |
|
572 |
# ---------- Step 1: Input Initial Idea ----------
|
|
|
|
|
573 |
if st.session_state.current_step == "input_idea":
|
574 |
st.header("Step 1: Input Product Idea")
|
575 |
with st.form(key="idea_form"):
|
@@ -626,14 +631,21 @@ if st.session_state.current_step == "refine_idea":
|
|
626 |
|
627 |
# --- Sub-Step 2a: Generate Clarifying Questions ---
|
628 |
if st.session_state.refinement_sub_step == "generate_questions":
|
629 |
-
st.info(f"Using **{st.session_state.model_choice}** to generate
|
630 |
-
with st.spinner("AI is
|
631 |
|
632 |
-
# --- PROMPT: Define the Question Generation Prompt ---
|
|
|
633 |
question_gen_prompt = f"""
|
634 |
-
# --- PROMPT: Insert your Question Generation prompt here ---
|
635 |
-
# Example:
|
636 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
637 |
|
638 |
**Initial Product Idea:**
|
639 |
---
|
@@ -644,8 +656,9 @@ if st.session_state.current_step == "refine_idea":
|
|
644 |
"""
|
645 |
# --- End Question Generation Prompt ---
|
646 |
|
647 |
-
|
648 |
-
|
|
|
649 |
|
650 |
questions_raw = generate_with_selected_model(
|
651 |
st.session_state.model_choice,
|
@@ -655,62 +668,55 @@ if st.session_state.current_step == "refine_idea":
|
|
655 |
)
|
656 |
|
657 |
if questions_raw and not questions_raw.startswith("Error:"):
|
658 |
-
# Parse the questions
|
659 |
st.session_state.clarifying_questions = [
|
660 |
q.strip('- ') for q in questions_raw.strip().split('\n') if q.strip() and q.strip() != '-'
|
661 |
]
|
662 |
if st.session_state.clarifying_questions:
|
663 |
-
st.session_state.user_answers = {i: "" for i in range(len(st.session_state.clarifying_questions))}
|
664 |
st.session_state.refinement_sub_step = "await_answers"
|
665 |
else:
|
666 |
st.warning("AI generated questions but they seem empty or incorrectly formatted. Proceeding without Q&A.")
|
667 |
-
# Fallback: Skip Q&A and go directly to final refinement based only on initial idea
|
668 |
st.session_state.refinement_sub_step = "generate_final_refinement"
|
669 |
-
st.session_state.clarifying_questions = []
|
670 |
st.session_state.user_answers = {}
|
671 |
-
|
672 |
else:
|
673 |
st.error("Failed to generate clarifying questions. Check API errors or model selection.")
|
674 |
st.warning("Proceeding to refine based only on the initial idea (skipping Q&A).")
|
675 |
-
st.session_state.refinement_sub_step = "generate_final_refinement"
|
676 |
-
st.session_state.clarifying_questions = []
|
677 |
st.session_state.user_answers = {}
|
678 |
|
679 |
-
st.session_state.processing = False
|
680 |
safe_rerun()
|
681 |
|
682 |
# --- Sub-Step 2b: Display Questions and Collect Answers ---
|
683 |
elif st.session_state.refinement_sub_step == "await_answers":
|
684 |
-
st.info("
|
685 |
with st.form("answers_form"):
|
686 |
# Display generated questions and input fields for answers
|
687 |
for i, question in enumerate(st.session_state.clarifying_questions):
|
688 |
st.session_state.user_answers[i] = st.text_area(
|
689 |
-
f"
|
690 |
key=f"answer_{i}",
|
691 |
-
value=st.session_state.user_answers.get(i, ""),
|
692 |
height=100
|
693 |
)
|
694 |
|
695 |
submit_answers_button = st.form_submit_button(
|
696 |
-
"➡️ Submit
|
697 |
use_container_width=True,
|
698 |
disabled=st.session_state.processing
|
699 |
)
|
700 |
|
701 |
if submit_answers_button:
|
702 |
-
# Basic check if answers are provided (optional)
|
703 |
-
# if not all(st.session_state.user_answers.values()):
|
704 |
-
# st.warning("Please try to answer all questions for the best result.")
|
705 |
-
# else:
|
706 |
st.session_state.refinement_sub_step = "generate_final_refinement"
|
707 |
-
st.session_state.processing = True
|
708 |
safe_rerun()
|
709 |
|
710 |
# Option to go back
|
711 |
if st.button("⬅️ Back to Idea Input (Discard Q&A)", disabled=st.session_state.processing):
|
712 |
st.session_state.current_step = "input_idea"
|
713 |
-
# Clear Q&A state
|
714 |
st.session_state.clarifying_questions = []
|
715 |
st.session_state.user_answers = {}
|
716 |
safe_rerun()
|
@@ -718,20 +724,21 @@ if st.session_state.current_step == "refine_idea":
|
|
718 |
|
719 |
# --- Sub-Step 2c: Generate Final Refined Description (using Q&A) ---
|
720 |
elif st.session_state.refinement_sub_step == "generate_final_refinement":
|
721 |
-
st.info(f"Using **{st.session_state.model_choice}** to generate the final refined description based on the idea and your
|
722 |
with st.spinner("AI is synthesizing the refined description..."):
|
723 |
|
724 |
# Prepare Q&A string for the prompt
|
725 |
qa_summary = "\n".join([
|
726 |
f"Q: {st.session_state.clarifying_questions[i]}\nA: {answer}"
|
727 |
-
for i, answer in st.session_state.user_answers.items() if answer
|
728 |
-
]) if st.session_state.user_answers else "No questions were answered."
|
729 |
|
730 |
|
731 |
# --- PROMPT: Define the Final Refinement Prompt (using Q&A) ---
|
|
|
732 |
final_refinement_prompt = f"""
|
733 |
# --- PROMPT: Insert your Final Refinement prompt (using Q&A) here ---
|
734 |
-
# Example: Based on the initial product idea, user preferences, and the
|
735 |
# --- End Final Refinement Prompt ---
|
736 |
|
737 |
**Initial Product Idea:**
|
@@ -741,15 +748,15 @@ if st.session_state.current_step == "refine_idea":
|
|
741 |
**Optional Preferences/Hints Provided:**
|
742 |
{st.session_state.tech_stack_hint if st.session_state.tech_stack_hint else "None provided"}
|
743 |
---
|
744 |
-
**
|
745 |
---
|
746 |
{qa_summary}
|
747 |
---
|
748 |
"""
|
749 |
# --- End Final Refinement Prompt ---
|
750 |
|
751 |
-
system_message_final_refine = "You are an AI assistant synthesizing information into a
|
752 |
-
max_tokens_final_refine = 1500
|
753 |
|
754 |
final_refined_content = generate_with_selected_model(
|
755 |
st.session_state.model_choice,
|
@@ -760,21 +767,22 @@ if st.session_state.current_step == "refine_idea":
|
|
760 |
|
761 |
if final_refined_content and not final_refined_content.startswith("Error:"):
|
762 |
st.session_state.refined_idea_content = final_refined_content
|
763 |
-
st.session_state.current_step = "review_idea"
|
764 |
else:
|
765 |
st.error("Failed to generate the final refined description.")
|
766 |
-
# Option to retry or go back might be added here
|
767 |
st.session_state.current_step = "input_idea" # Go back if failed
|
768 |
|
769 |
-
st.session_state.processing = False
|
770 |
safe_rerun()
|
771 |
|
772 |
|
773 |
# ---------- Step 3: Review and Confirm Final Idea ----------
|
|
|
|
|
774 |
if st.session_state.current_step == "review_idea":
|
775 |
st.header("Step 3: Review and Confirm Final Refined Idea")
|
776 |
if st.session_state.refined_idea_content:
|
777 |
-
st.info("Review the AI's final refined description below (generated
|
778 |
|
779 |
# Display Q&A for context if available
|
780 |
if st.session_state.clarifying_questions and st.session_state.user_answers:
|
@@ -835,6 +843,8 @@ if st.session_state.current_step == "review_idea":
|
|
835 |
|
836 |
|
837 |
# ---------- Step 4: Select and Generate Documents ----------
|
|
|
|
|
838 |
if st.session_state.current_step == "generate_docs":
|
839 |
st.header("Step 4: Generate Product Documents")
|
840 |
if st.session_state.confirmed_idea_content:
|
@@ -945,6 +955,8 @@ if st.session_state.current_step == "generate_docs":
|
|
945 |
|
946 |
|
947 |
# ---------- Step 5: Display Generated Documents ----------
|
|
|
|
|
948 |
if st.session_state.current_step == "display_docs":
|
949 |
st.header("Step 5: Generated Documents")
|
950 |
|
@@ -1016,8 +1028,9 @@ if st.session_state.current_step == "display_docs":
|
|
1016 |
|
1017 |
|
1018 |
# ---------- Footer ----------
|
|
|
|
|
1019 |
st.markdown("---")
|
1020 |
footer_model_choice = st.session_state.get('model_choice', 'N/A')
|
1021 |
st.caption(f"Using model: **{footer_model_choice}** | Workflow Step: **{st.session_state.get('current_step', 'N/A')}**"
|
1022 |
f"{' (Sub-step: ' + st.session_state.get('refinement_sub_step', 'N/A') + ')' if st.session_state.get('current_step') == 'refine_idea' else ''}")
|
1023 |
-
|
|
|
150 |
response = client.chat.completions.create(
|
151 |
model=model_id,
|
152 |
messages=messages,
|
153 |
+
temperature=0.7, # Slightly higher temp might encourage more diverse options
|
154 |
max_tokens=max_tokens
|
155 |
)
|
156 |
return response.choices[0].message.content
|
|
|
169 |
'HARM_CATEGORY_SEXUALLY_EXPLICIT': 'block_none',
|
170 |
'HARM_CATEGORY_DANGEROUS_CONTENT': 'block_none',
|
171 |
},
|
172 |
+
generation_config=client.types.GenerationConfig(temperature=0.8) # Slightly higher temp
|
173 |
)
|
174 |
response = model.generate_content(full_prompt)
|
175 |
|
|
|
206 |
payload = {
|
207 |
"model": model_id,
|
208 |
"messages": messages,
|
209 |
+
"temperature": 0.7, # Slightly higher temp
|
210 |
"max_tokens": max_tokens
|
211 |
}
|
212 |
try:
|
|
|
235 |
system=system_message if system_message else None,
|
236 |
messages=[
|
237 |
{"role": "user", "content": prompt}
|
238 |
+
],
|
239 |
+
temperature=0.8 # Slightly higher temp
|
240 |
)
|
241 |
content = ""
|
242 |
if message.content:
|
|
|
353 |
)
|
354 |
|
355 |
# ---------- Initialize Session State (Provibe Workflow) ----------
|
356 |
+
# (Session state initialization remains the same as the previous version)
|
357 |
+
# ... (Omitted for brevity) ...
|
358 |
# Core workflow steps
|
359 |
if 'current_step' not in st.session_state:
|
360 |
st.session_state.current_step = "input_idea" # input_idea -> refine_idea -> review_idea -> generate_docs -> display_docs
|
|
|
558 |
|
559 |
# ---------- UI Layout (Provibe Workflow) ----------
|
560 |
st.set_page_config(layout="wide", page_title="Provibe Prompt Tester")
|
561 |
+
st.title("🧪 Provibe Prompt Tester (with Divergent Q&A Refinement)") # Updated Title
|
562 |
+
st.caption("Test and refine prompts for the Provibe document generation workflow, including interactive Q&A exploring possibilities.")
|
563 |
|
564 |
# Display API Key Errors
|
565 |
if secret_errors:
|
|
|
573 |
# --- Workflow Steps ---
|
574 |
|
575 |
# ---------- Step 1: Input Initial Idea ----------
|
576 |
+
# (This step remains the same as the previous version)
|
577 |
+
# ... (Omitted for brevity) ...
|
578 |
if st.session_state.current_step == "input_idea":
|
579 |
st.header("Step 1: Input Product Idea")
|
580 |
with st.form(key="idea_form"):
|
|
|
631 |
|
632 |
# --- Sub-Step 2a: Generate Clarifying Questions ---
|
633 |
if st.session_state.refinement_sub_step == "generate_questions":
|
634 |
+
st.info(f"Using **{st.session_state.model_choice}** to generate questions exploring possibilities. Please wait.")
|
635 |
+
with st.spinner("AI is brainstorming questions..."):
|
636 |
|
637 |
+
# --- PROMPT: Define the Question Generation Prompt (Divergent Thinking) ---
|
638 |
+
# ***** MODIFIED PROMPT *****
|
639 |
question_gen_prompt = f"""
|
640 |
+
# --- PROMPT: Insert your Divergent Question Generation prompt here ---
|
641 |
+
# Example: Analyze the initial product idea and hints below. Instead of just asking for details, generate 3-5 questions that present potential *alternative directions, implementation types, or core feature variations* for this idea. Frame the questions to encourage the user to consider different possibilities (e.g., "Would this be more like X or Y?", "Should the focus be A or B?", "Could this also incorporate Z?").
|
642 |
+
# The goal is to help the user explore the solution space before refining.
|
643 |
+
# Output *only* the questions, each on a new line, starting with '- '. Do not include numbering or any other text.
|
644 |
+
# Example for 'water intake tracker':
|
645 |
+
# - Should this primarily be a mobile app for manual logging, or involve a physical smart device (like a bottle)?
|
646 |
+
# - Would users benefit more from simple reminders, or personalized coaching based on their habits?
|
647 |
+
# - Could gamification (streaks, badges) be a core motivator, or should it focus purely on utility?
|
648 |
+
# --- End Divergent Question Generation Prompt ---
|
649 |
|
650 |
**Initial Product Idea:**
|
651 |
---
|
|
|
656 |
"""
|
657 |
# --- End Question Generation Prompt ---
|
658 |
|
659 |
+
# System message can remain similar or be adjusted slightly
|
660 |
+
system_message_qa = "You are an AI assistant skilled at brainstorming alternative product directions and framing them as clarifying questions."
|
661 |
+
max_tokens_qa = 400 # Allow slightly more tokens for potentially more complex questions
|
662 |
|
663 |
questions_raw = generate_with_selected_model(
|
664 |
st.session_state.model_choice,
|
|
|
668 |
)
|
669 |
|
670 |
if questions_raw and not questions_raw.startswith("Error:"):
|
671 |
+
# Parse the questions
|
672 |
st.session_state.clarifying_questions = [
|
673 |
q.strip('- ') for q in questions_raw.strip().split('\n') if q.strip() and q.strip() != '-'
|
674 |
]
|
675 |
if st.session_state.clarifying_questions:
|
676 |
+
st.session_state.user_answers = {i: "" for i in range(len(st.session_state.clarifying_questions))}
|
677 |
st.session_state.refinement_sub_step = "await_answers"
|
678 |
else:
|
679 |
st.warning("AI generated questions but they seem empty or incorrectly formatted. Proceeding without Q&A.")
|
|
|
680 |
st.session_state.refinement_sub_step = "generate_final_refinement"
|
681 |
+
st.session_state.clarifying_questions = []
|
682 |
st.session_state.user_answers = {}
|
|
|
683 |
else:
|
684 |
st.error("Failed to generate clarifying questions. Check API errors or model selection.")
|
685 |
st.warning("Proceeding to refine based only on the initial idea (skipping Q&A).")
|
686 |
+
st.session_state.refinement_sub_step = "generate_final_refinement"
|
687 |
+
st.session_state.clarifying_questions = []
|
688 |
st.session_state.user_answers = {}
|
689 |
|
690 |
+
st.session_state.processing = False
|
691 |
safe_rerun()
|
692 |
|
693 |
# --- Sub-Step 2b: Display Questions and Collect Answers ---
|
694 |
elif st.session_state.refinement_sub_step == "await_answers":
|
695 |
+
st.info("Consider these possibilities and answer the questions to guide the refinement:") # Updated info text
|
696 |
with st.form("answers_form"):
|
697 |
# Display generated questions and input fields for answers
|
698 |
for i, question in enumerate(st.session_state.clarifying_questions):
|
699 |
st.session_state.user_answers[i] = st.text_area(
|
700 |
+
f"🤔 {question}", # Changed icon
|
701 |
key=f"answer_{i}",
|
702 |
+
value=st.session_state.user_answers.get(i, ""),
|
703 |
height=100
|
704 |
)
|
705 |
|
706 |
submit_answers_button = st.form_submit_button(
|
707 |
+
"➡️ Submit Choices & Generate Refined Description", # Updated button text
|
708 |
use_container_width=True,
|
709 |
disabled=st.session_state.processing
|
710 |
)
|
711 |
|
712 |
if submit_answers_button:
|
|
|
|
|
|
|
|
|
713 |
st.session_state.refinement_sub_step = "generate_final_refinement"
|
714 |
+
st.session_state.processing = True
|
715 |
safe_rerun()
|
716 |
|
717 |
# Option to go back
|
718 |
if st.button("⬅️ Back to Idea Input (Discard Q&A)", disabled=st.session_state.processing):
|
719 |
st.session_state.current_step = "input_idea"
|
|
|
720 |
st.session_state.clarifying_questions = []
|
721 |
st.session_state.user_answers = {}
|
722 |
safe_rerun()
|
|
|
724 |
|
725 |
# --- Sub-Step 2c: Generate Final Refined Description (using Q&A) ---
|
726 |
elif st.session_state.refinement_sub_step == "generate_final_refinement":
|
727 |
+
st.info(f"Using **{st.session_state.model_choice}** to generate the final refined description based on the idea and your chosen direction. Please wait.") # Updated info text
|
728 |
with st.spinner("AI is synthesizing the refined description..."):
|
729 |
|
730 |
# Prepare Q&A string for the prompt
|
731 |
qa_summary = "\n".join([
|
732 |
f"Q: {st.session_state.clarifying_questions[i]}\nA: {answer}"
|
733 |
+
for i, answer in st.session_state.user_answers.items() if answer
|
734 |
+
]) if st.session_state.clarifying_questions and st.session_state.user_answers else "No questions were asked or answered."
|
735 |
|
736 |
|
737 |
# --- PROMPT: Define the Final Refinement Prompt (using Q&A) ---
|
738 |
+
# This prompt might need less adjustment, but ensure it tells the AI to *use* the answers to shape the description
|
739 |
final_refinement_prompt = f"""
|
740 |
# --- PROMPT: Insert your Final Refinement prompt (using Q&A) here ---
|
741 |
+
# Example: Based on the initial product idea, user preferences, and the direction indicated by the user's answers to the clarifying questions below, generate a concise yet comprehensive 'Refined Product Description'. Synthesize all the information, *especially incorporating the choices made in the Q&A*, into a well-structured description covering the core value proposition, key features, target audience, and chosen technical/functional approach. This description will be the basis for generating all subsequent documents.
|
742 |
# --- End Final Refinement Prompt ---
|
743 |
|
744 |
**Initial Product Idea:**
|
|
|
748 |
**Optional Preferences/Hints Provided:**
|
749 |
{st.session_state.tech_stack_hint if st.session_state.tech_stack_hint else "None provided"}
|
750 |
---
|
751 |
+
**Questions Exploring Possibilities & User's Direction:**
|
752 |
---
|
753 |
{qa_summary}
|
754 |
---
|
755 |
"""
|
756 |
# --- End Final Refinement Prompt ---
|
757 |
|
758 |
+
system_message_final_refine = "You are an AI assistant synthesizing information and user choices into a focused product specification."
|
759 |
+
max_tokens_final_refine = 1500
|
760 |
|
761 |
final_refined_content = generate_with_selected_model(
|
762 |
st.session_state.model_choice,
|
|
|
767 |
|
768 |
if final_refined_content and not final_refined_content.startswith("Error:"):
|
769 |
st.session_state.refined_idea_content = final_refined_content
|
770 |
+
st.session_state.current_step = "review_idea"
|
771 |
else:
|
772 |
st.error("Failed to generate the final refined description.")
|
|
|
773 |
st.session_state.current_step = "input_idea" # Go back if failed
|
774 |
|
775 |
+
st.session_state.processing = False
|
776 |
safe_rerun()
|
777 |
|
778 |
|
779 |
# ---------- Step 3: Review and Confirm Final Idea ----------
|
780 |
+
# (This step remains largely the same, but the context expander is more relevant now)
|
781 |
+
# ... (Omitted for brevity, assume it's the same as before) ...
|
782 |
if st.session_state.current_step == "review_idea":
|
783 |
st.header("Step 3: Review and Confirm Final Refined Idea")
|
784 |
if st.session_state.refined_idea_content:
|
785 |
+
st.info("Review the AI's final refined description below (generated based on your chosen direction). Edit it as needed. This **final text** will be used to generate all documents.")
|
786 |
|
787 |
# Display Q&A for context if available
|
788 |
if st.session_state.clarifying_questions and st.session_state.user_answers:
|
|
|
843 |
|
844 |
|
845 |
# ---------- Step 4: Select and Generate Documents ----------
|
846 |
+
# (This step remains the same as the previous version)
|
847 |
+
# ... (Omitted for brevity) ...
|
848 |
if st.session_state.current_step == "generate_docs":
|
849 |
st.header("Step 4: Generate Product Documents")
|
850 |
if st.session_state.confirmed_idea_content:
|
|
|
955 |
|
956 |
|
957 |
# ---------- Step 5: Display Generated Documents ----------
|
958 |
+
# (This step remains the same as the previous version)
|
959 |
+
# ... (Omitted for brevity) ...
|
960 |
if st.session_state.current_step == "display_docs":
|
961 |
st.header("Step 5: Generated Documents")
|
962 |
|
|
|
1028 |
|
1029 |
|
1030 |
# ---------- Footer ----------
|
1031 |
+
# (This section remains the same as the previous version)
|
1032 |
+
# ... (Omitted for brevity) ...
|
1033 |
st.markdown("---")
|
1034 |
footer_model_choice = st.session_state.get('model_choice', 'N/A')
|
1035 |
st.caption(f"Using model: **{footer_model_choice}** | Workflow Step: **{st.session_state.get('current_step', 'N/A')}**"
|
1036 |
f"{' (Sub-step: ' + st.session_state.get('refinement_sub_step', 'N/A') + ')' if st.session_state.get('current_step') == 'refine_idea' else ''}")
|
|