qstn_gui / src /pages /00_Tutorial.py
AhmedASalem's picture
Sync GUI from GitHub: tutorial, default placeholders, help links
fd01b80
"""
Tutorial & Help page for the QSTN GUI.
Use ?section=start|options|prompts|inference|overview to deep-link to a section.
"""
import streamlit as st
st.set_page_config(page_title="Tutorial & Help", layout="wide")
st.title("πŸ“– QSTN Tutorial & Help")
st.markdown("Full documentation of every option in the GUI. Use the sections below or jump to a step from the links.")
# Section keys for deep linking
SECTION_START = "start"
SECTION_OPTIONS = "options"
SECTION_PROMPTS = "prompts"
SECTION_INFERENCE = "inference"
SECTION_OVERVIEW = "overview"
section_param = st.query_params.get("section", SECTION_START)
# Quick jump links
st.markdown("**Jump to:** ")
cols = st.columns(5)
with cols[0]:
st.page_link("pages/00_Tutorial.py", label="Start", query_params={"section": SECTION_START})
with cols[1]:
st.page_link("pages/00_Tutorial.py", label="Answer options", query_params={"section": SECTION_OPTIONS})
with cols[2]:
st.page_link("pages/00_Tutorial.py", label="Prompts", query_params={"section": SECTION_PROMPTS})
with cols[3]:
st.page_link("pages/00_Tutorial.py", label="Inference", query_params={"section": SECTION_INFERENCE})
with cols[4]:
st.page_link("pages/00_Tutorial.py", label="Run & results", query_params={"section": SECTION_OVERVIEW})
st.divider()
# =============================================================================
# 1. START PAGE
# =============================================================================
with st.expander("**1. Start page – Sessions, questionnaire & population**", expanded=(section_param == SECTION_START)):
st.markdown("On the **Start** page you create or load a session, upload your data, and build the survey. All work is saved per session.")
st.markdown("**Session options** (no replica): Continue Last Session, Start New Session, Switch to another session (dropdown), Create New, View Saved Sessions (Load/Delete).")
st.subheader("Data upload")
with st.container(border=True):
_c1, _c2 = st.columns(2)
with _c1:
st.text_input("Select a questionnaire to start with", value="", disabled=True, key="tutorial_start_quest")
st.caption("File upload for the **questionnaire** CSV. One row per question. Required columns: `questionnaire_item_id`, `question_content`, `question_stem` (optional).")
with _c2:
st.text_input("Select a population to start with", value="", disabled=True, key="tutorial_start_pop")
st.caption("File upload for the **population** CSV. One row per persona. Required columns: `questionnaire_name`, `system_prompt`, `questionnaire_instruction`.")
st.button("Confirm and Prepare Questionnaire", disabled=True, key="tutorial_start_btn")
st.caption("Builds the survey and goes to **Answer options**. Use **Clear** under each upload to reset to example data.")
st.subheader("Next step")
st.page_link("Start_Page.py", label="β†’ Go to Start page")
# =============================================================================
# 2. ANSWER OPTIONS (LIKERT SCALE OPTIONS GENERATOR)
# =============================================================================
with st.expander("**2. Answer options – Likert scales & response format**", expanded=(section_param == SECTION_OPTIONS)):
st.markdown("This page configures how the LLM sees answer options and how the model outputs its answer (free text, JSON, or constrained choice).")
with st.container(border=True):
st.subheader("Main Configuration")
_col1, _col2, _col3 = st.columns(3)
with _col1:
st.number_input("Number of Options (n)", value=5, min_value=2, step=1, disabled=True, key="tutorial_opt_n")
st.caption("Total number of choices in the scale (e.g. 5 for a 5-point scale). Minimum 2.")
with _col2:
st.selectbox("Index Type", options=["integer", "char_low", "char_up"], index=0, disabled=True, key="tutorial_opt_idx")
st.caption("How options are labeled: integer (1,2,3…), char_low (a,b,c…), or char_up (A,B,C…).")
with _col3:
st.number_input("Starting Index", value=1, step=1, disabled=True, key="tutorial_opt_start")
st.caption("Number to start counting from (e.g. 1).")
st.subheader("Ordering and Structure")
_co1, _co2, _co3, _co4 = st.columns(4)
with _co1:
st.checkbox("From-To Scale Only", value=False, disabled=True, key="tutorial_opt_ft")
st.caption("Only first and last labels shown (e.g. β€œ1 Strongly Disagree to 5 Strongly Agree”). Requires exactly 2 answer texts.")
with _co2:
st.checkbox("Random Order", value=False, disabled=True, key="tutorial_opt_rand")
st.caption("Randomize option order per prompt. Cannot combine with Reversed Order.")
with _co3:
st.checkbox("Reversed Order", value=False, disabled=True, key="tutorial_opt_rev")
st.caption("Reverse option order (e.g. 5β†’1).")
with _co4:
st.checkbox("Even Order", value=False, disabled=True, key="tutorial_opt_even")
st.caption("If odd number of labels, middle option is removed.")
st.subheader("Answer Texts")
st.text_area("Enter Answer Texts (one per line)", value="Strongly Disagree\nDisagree\nNeutral\nAgree\nStrongly Agree", height=120, disabled=True, key="tutorial_opt_texts")
st.caption("One label per line. Number of lines must match **Number of Options** (or 2 if From-To Scale Only).")
st.subheader("Response Generation Method")
st.checkbox("Output Indices Only", value=False, disabled=True, key="tutorial_opt_outidx")
st.caption("If checked, model outputs only indices (e.g. 1, 2) instead of full text (e.g. 1: Strongly Disagree). Applies to all methods below.")
st.selectbox("Response Generation Method", options=["None", "JSON Single Answer", "JSON All Options (Probabilities)", "JSON with Reasoning", "Choice"], index=0, disabled=True, key="tutorial_opt_rgm")
st.markdown("**Details by option:**")
st.markdown("""
- **None** β€” Model returns free text; no structured output. You must parse the response yourself. No extra configuration on this page.
- **JSON Single Answer** β€” Model returns a single selected answer in JSON (e.g. one chosen option per question). Parsing is automatic; use when you want one answer per item.
- **JSON All Options (Probabilities)** β€” Model returns a probability (or score) for each option. Use when you want a distribution over choices rather than a single pick. Parsing expects this structure.
- **JSON with Reasoning** β€” Model returns reasoning text plus the chosen answer in JSON. Use when you need both explanation and a structured answer. Parsing extracts both.
- **Choice** β€” Model must pick exactly one value from a fixed list. When selected, **Allowed Choices** appears: one choice per line, either indices only (e.g. 1, 2, 3) or with labels (e.g. 1: Strongly Disagree). **Output Indices Only** controls whether the model returns indices or full labels. Parsing uses the same list.
""")
st.subheader("Next step")
st.page_link("pages/01_Option_Prompt.py", label="β†’ Go to Answer options")
# =============================================================================
# 3. PROMPT CONFIGURATION
# =============================================================================
with st.expander("**3. Prompt configuration – System prompt & instructions**", expanded=(section_param == SECTION_PROMPTS)):
st.markdown("Edit the system and user prompts the LLM sees. Use the checkboxes to copy the current prompts to all questionnaires on confirm.")
with st.container(border=True):
st.subheader("Configuration")
st.checkbox("On update: change all System Prompts", value=True, disabled=True, key="tutorial_prompt_chg_sys")
st.caption("When you click Confirm and Prepare Questionnaire, the current system prompt is applied to **all** questionnaires (missing placeholders added to others).")
st.checkbox("On update: change all questionnaire instructions", value=True, disabled=True, key="tutorial_prompt_chg_inst")
st.caption("Same for the main prompt (instructions); all questionnaires get this text.")
st.text_area("System prompt", value="You are a student.", height=80, disabled=True, key="tutorial_prompt_sys")
st.caption("The system message sent to the model (e.g. role or context).")
st.text_area("Prompt", value="Please answer the following questions.", height=80, disabled=True, key="tutorial_prompt_user")
st.caption("User-facing instructions given before the questions.")
st.write("**Insert Placeholder:**")
_pb1, _pb2, _pb3, _pb4 = st.columns(4)
with _pb1:
st.button("Prompt Questions", disabled=True, key="tutorial_prompt_btn_p")
with _pb2:
st.button("Prompt Options", disabled=True, key="tutorial_prompt_btn_o")
with _pb3:
st.button("Automatic Output", disabled=True, key="tutorial_prompt_btn_a")
with _pb4:
st.button("JSON Template", disabled=True, key="tutorial_prompt_btn_j")
st.caption("Type shortcut (-P, -O, -A, -J) in system or main prompt, then click to replace with placeholder. **Prompt Questions**: where question text goes. **Prompt Options**: where answer options go (configure options first). **Automatic Output**: JSON/output instructions. **JSON Template**: JSON schema for response.")
st.text_area("Question Stem", value="How do you feel about?", height=80, disabled=True, key="tutorial_prompt_stem")
st.caption("Template for each question. Use **Question Content** placeholder (-Q) so the actual item (e.g. β€œCoffee”) is inserted.")
st.checkbox("Randomize the order of items", value=False, disabled=True, key="tutorial_prompt_rand")
st.caption("If checked, question order is randomized per questionnaire.")
st.caption("**Live Preview** (on the real page) shows prompts with placeholders filled; use **Update Preview** to refresh. The **paginator** switches which questionnaire you edit.")
st.subheader("Next step")
st.page_link("pages/02_Prompt_Configuration.py", label="β†’ Go to Prompt configuration")
# =============================================================================
# 4. INFERENCE SETTINGS
# =============================================================================
with st.expander("**4. Inference settings – API client & model**", expanded=(section_param == SECTION_INFERENCE)):
st.markdown("Configure where the LLM is called (API URL, key) and how (model, temperature, etc.). Values are used on Final overview when you run the survey.")
_inf_col1, _inf_col2 = st.columns(2)
with _inf_col1:
with st.container(border=True):
st.subheader("1. Client Configuration")
st.text_input("API Key", value="", type="password", disabled=True, key="tutorial_inf_apikey")
st.caption("Your OpenAI API key (or leave empty for local vLLM). Handled securely.")
st.text_input("Base URL", value="", placeholder="https://api.openai.com/v1", disabled=True, key="tutorial_inf_base")
st.caption("API base URL. For **local vLLM** use `http://localhost:8000/v1`. Optional: Organization ID, Project ID.")
st.number_input("Timeout (seconds)", value=20, min_value=1, disabled=True, key="tutorial_inf_timeout")
st.caption("Request timeout in seconds.")
st.number_input("Max Retries", value=2, min_value=0, disabled=True, key="tutorial_inf_retries")
st.caption("Number of retries for failed requests. Advanced: JSON for default_headers, default_query, etc.")
with _inf_col2:
with st.container(border=True):
st.subheader("2. Inference Configuration")
st.text_input("Model Name", value="", placeholder="meta-llama/Llama-3.1-70B-Instruct", disabled=True, key="tutorial_inf_model")
st.caption("Model ID. Must match your API/vLLM (e.g. `meta-llama/Llama-3.2-3B-Instruct` for vLLM).")
st.slider("Temperature", 0.0, 2.0, 1.0, 0.01, disabled=True, key="tutorial_inf_temp")
st.caption("Randomness (0–2). Lower = more deterministic.")
st.number_input("Max Tokens", value=1024, min_value=1, disabled=True, key="tutorial_inf_maxtok")
st.caption("Maximum tokens to generate per completion.")
st.slider("Top P", 0.0, 1.0, 1.0, 0.01, disabled=True, key="tutorial_inf_topp")
st.caption("Nucleus sampling: consider tokens with cumulative probability up to this value.")
st.number_input("Seed", value=42, min_value=0, disabled=True, key="tutorial_inf_seed")
st.caption("Seed for reproducible sampling. Advanced: JSON for stop, presence_penalty, frequency_penalty, logit_bias.")
st.subheader("Using local vLLM")
st.markdown("""
1. Start the vLLM server, e.g.:
```bash
python -m vllm.entrypoints.openai.api_server --model meta-llama/Llama-3.2-3B-Instruct --max-model-len 8192
```
2. In the GUI: set **Base URL** to `http://localhost:8000/v1` and **Model name** to the same model ID (e.g. `meta-llama/Llama-3.2-3B-Instruct`).
3. Click **Generate Configuration & Code** to save; then go to **Final overview** to run the survey.
""")
st.subheader("Next step")
st.markdown("Click **Generate Configuration & Code** to save client and inference config and go to **Final overview**.")
st.page_link("pages/03_Inference_Setting.py", label="β†’ Go to Inference settings")
# =============================================================================
# 5. FINAL OVERVIEW & RUN
# =============================================================================
with st.expander("**5. Final overview – Run survey & save results**", expanded=(section_param == SECTION_OVERVIEW)):
st.markdown("Read-only inference summary, prompt preview for the selected method, then run the survey and save results.")
with st.container(border=True):
st.subheader("βš™οΈ Inference Parameters")
st.text_input("Model Name", value="meta-llama/Llama-3.2-3B-Instruct", disabled=True, key="tutorial_overview_model")
st.caption("Shown from Inference settings (not editable here). Change on **Inference settings** page.")
st.slider("Temperature", 0.0, 2.0, 1.0, 0.01, disabled=True, key="tutorial_overview_temp")
st.number_input("Max Tokens", value=1024, min_value=1, disabled=True, key="tutorial_overview_maxtok")
st.selectbox("Questionnaire Method", options=["Single item", "Battery", "Sequential"], index=0, disabled=True, key="tutorial_overview_method")
st.caption("**Single item**: one question per API call. **Battery**: all questions in one call. **Sequential**: all in one thread with conversation history. Affects prompts and result parsing.")
st.markdown("**Details by option:**")
st.markdown("""
- **Single item** β€” One question is sent per API call; the model sees only that question (and system/instruction context). Preview on the page shows the first few items so you can check how each is prompted.
- **Battery** β€” All questions are sent in one API call; the model sees the full list and responds once. Preview shows one combined prompt.
- **Sequential** β€” All questions are sent in one conversation thread with history; the model sees prior Q&A. Preview shows one combined flow. Parsing respects the conversation structure.
""")
st.caption("**Live preview** (on the real page): for Single item, first few items shown; for Battery/Sequential, one combined preview. **Paginator** switches questionnaire.")
with st.container(border=True):
st.subheader("πŸ’Ύ Save Results")
st.text_input("Save File", value="questionnaire_results.csv", disabled=True, key="tutorial_overview_savefile")
st.caption("Filename for the CSV. If you omit `.csv`, it is appended.")
st.button("Save Results", disabled=True, key="tutorial_overview_savebtn")
st.caption("Downloads the results table as CSV. Appears after you click **Confirm and Run Questionnaire** and inference finishes.")
st.page_link("pages/04_Final_Overview.py", label="β†’ Go to Final overview")
st.divider()
st.subheader("Workflow summary")
st.markdown("**Start** (upload data, confirm) β†’ **Answer options** (scale + response method, confirm) β†’ **Prompt configuration** (prompts + placeholders, confirm) β†’ **Inference settings** (API + model, generate config) β†’ **Final overview** (run & save).")