| Server IP : 3.147.158.171 / Your IP : 216.73.216.216 Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5 System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64 User : ec2-user ( 1000) PHP Version : 8.4.21 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /tsai/repo/api/app/services/ |
Upload File : |
import logging
from app.appTypes import Character
# Per-difficulty quiz settings. The number of questions, the pass threshold, and
# whether hints are offered all scale with difficulty. Points are awarded by
# WordPress (mapped server-side from difficulty), not here.
QUIZ_DIFFICULTIES = {
'easy': {
'count': 3,
'count_word': 'three',
# At least this many correct to pass.
'pass_threshold': 2,
'pass_threshold_word': 'two',
'hints': True,
},
'advanced': {
'count': 5,
'count_word': 'five',
'pass_threshold': 4,
'pass_threshold_word': 'four',
'hints': False,
},
}
DEFAULT_DIFFICULTY = 'easy'
class QuizService:
def _difficulty_settings(self, difficulty: str) -> dict:
return QUIZ_DIFFICULTIES.get(difficulty, QUIZ_DIFFICULTIES[DEFAULT_DIFFICULTY])
def get_quiz_instructions(
self, character: Character, title: str, body: str, difficulty: str = DEFAULT_DIFFICULTY
) -> str:
d = self._difficulty_settings(difficulty)
count_word = d['count_word']
threshold_word = d['pass_threshold_word']
if d['hints']:
hint_rule = "- If the reader is stuck, give a gentle hint rather than the answer.\n"
else:
hint_rule = (
"- This is the ADVANCED quiz: do NOT give hints. If the reader is stuck, "
"encourage them to give their best answer, but never reveal or hint at the answer.\n"
)
instructions = (
f"Your name is {character.name}. {character.description}\n\n"
f"You are giving the reader an approachable {count_word}-question quiz on the article below. "
"The goal is for the reader to PASS the quiz with a perfect or near-perfect score — "
"this is meant to be encouraging, not punishing.\n\n"
"Rules:\n"
f"- Ask exactly {count_word} questions, ONE AT A TIME, waiting for the reader's answer before moving on.\n"
"- Each question MUST be either:\n"
" (a) MULTIPLE CHOICE: present 3-4 labeled options (A, B, C, D) and ask the reader to pick one, OR\n"
" (b) SHORT ANSWER: a question whose answer is at most 1-2 words (a name, a number, a single term).\n"
"- Never ask open-ended or essay questions. The reader should be able to answer in seconds.\n"
f"- Mix the two formats across the {count_word} questions when it makes sense.\n"
"- Cover the most important factual content of the article.\n"
"- Be warm, patient, and encouraging throughout. Celebrate correct answers.\n"
"- Accept reasonable variations in spelling, capitalization, and phrasing for short answers.\n"
"- For multiple choice, accept either the letter (e.g., 'B') or the option text.\n"
f"{hint_rule}"
"- The reader can restart the quiz any time, as many times as they want — remind them of this if they seem frustrated.\n"
"- The reader's result is NOT recorded unless they explicitly ask for it to be recorded.\n"
f"- After the final answer, summarize how they did:\n"
f" - If all {count_word} are correct: tell them they PASSED with a perfect score and congratulate them warmly.\n"
f" - If at least {threshold_word} are correct: tell them they PASSED and congratulate them.\n"
" - Otherwise: encourage them, briefly mention which areas they might review, and invite them to try the quiz again.\n"
"- Do NOT assign letter grades, points, or percentages. Just give friendly, conversational feedback.\n\n"
"Status reporting (IMPORTANT):\n"
"- You have access to a tool called `set_quiz_status`. Call it whenever the reader's overall quiz status changes.\n"
f"- Call it with status='passed' ONLY in the same turn where you announce the reader has passed (at least {threshold_word} of {count_word} correct).\n"
"- Call it with status='failed' ONLY if the reader explicitly gives up.\n"
"- Otherwise do not call the tool — the default state is in_progress.\n"
"- The tool call is a silent control signal; the user does not see it. Always send your normal chat reply alongside it.\n\n"
f"Article title: {title}\n\n"
"Article body:\n"
f"{body}\n"
)
logging.debug('quiz instructions:\n%s', instructions)
return instructions
def get_quiz_intro_message(
self, character: Character, title: str, difficulty: str = DEFAULT_DIFFICULTY
) -> str:
d = self._difficulty_settings(difficulty)
count_word = d['count_word']
return (
f"Hi! I'm {character.name}, and I'd love to give you a quick {count_word}-question quiz on "
f"“{title}”. The questions are multiple choice or one-or-two-word answers, so it should "
"be quick. You can restart any time, and your result won't be recorded unless you ask me to record it. "
"Ready for question 1?"
)