@@ -782,18 +782,24 @@ def get_async_explainer():
782782 div_id = request .vars .div_id
783783
784784 messages = db (
785- (db .useinfo .event == "sendmessage" )
785+ (db .useinfo .event . belongs ([ "sendmessage" , "reflection" ]) )
786786 & (db .useinfo .div_id == div_id )
787787 & (db .useinfo .course_id == course_name )
788788 ).select (orderby = db .useinfo .id )
789789
790- seen = {}
790+ all_msgs = [] #list of (sid, msg) in insertion order
791+ last_per_sid = {}
791792 for row in messages :
792- try :
793- msg = row .act .split (":" , 2 )[2 ]
794- except Exception :
793+ if row .event == "reflection" :
795794 msg = row .act
796- seen [row .sid ] = msg
795+ else :
796+ try :
797+ msg = row .act .split (":" , 2 )[2 ]
798+ except Exception :
799+ msg = row .act
800+ if last_per_sid .get (row .sid ) != msg : #skip exact consecutive duplicates only
801+ all_msgs .append ((row .sid , msg ))
802+ last_per_sid [row .sid ] = msg
797803
798804 llm_turns = db (
799805 (db .useinfo .event == "pi_llm_turn" )
@@ -802,7 +808,6 @@ def get_async_explainer():
802808 ).select (orderby = db .useinfo .id )
803809
804810 llm_by_sid = {}
805- sid_order = []
806811 for row in llm_turns :
807812 try :
808813 turn = json .loads (row .act )
@@ -812,22 +817,18 @@ def get_async_explainer():
812817 content = turn .get ("content" , "" )
813818 if row .sid not in llm_by_sid :
814819 llm_by_sid [row .sid ] = {}
815- sid_order .append (row .sid )
816820 if attempt_id not in llm_by_sid [row .sid ]:
817821 llm_by_sid [row .sid ][attempt_id ] = []
818822 llm_by_sid [row .sid ][attempt_id ].append ((turn_index , role , content ))
819823 except Exception :
820824 pass
821825
822- for sid in seen :
823- if sid not in llm_by_sid :
824- sid_order .append (sid )
825-
826826 parts = []
827- for sid in sid_order :
828- if sid in seen :
829- parts .append (f"<li><strong>{ sid } </strong> said: { seen [sid ]} </li>" )
830- if sid in llm_by_sid :
827+ sids_with_llm_shown = set ()
828+ for sid , msg in all_msgs :
829+ parts .append (f"<li><strong>{ sid } </strong> said: { msg } </li>" )
830+ if sid in llm_by_sid and sid not in sids_with_llm_shown :
831+ sids_with_llm_shown .add (sid )
831832 latest_attempt = max (
832833 llm_by_sid [sid ].keys (),
833834 key = lambda a : max (t [0 ] for t in llm_by_sid [sid ][a ])
@@ -837,6 +838,14 @@ def get_async_explainer():
837838 if role == "assistant" :
838839 parts .append (f"<li><strong>LLM Peer</strong> said: { content } </li>" )
839840
841+ for sid , attempts in llm_by_sid .items ():
842+ if sid not in sids_with_llm_shown and not any (s == sid for s , _ in all_msgs ):
843+ latest_attempt = max (attempts .keys (), key = lambda a : max (t [0 ] for t in attempts [a ]))
844+ turns = sorted (attempts [latest_attempt ], key = lambda t : t [0 ])
845+ for _ , role , content in turns :
846+ if role == "assistant" :
847+ parts .append (f"<li><strong>LLM Peer</strong> said: { content } </li>" )
848+
840849 if not parts :
841850 mess = "Sorry there are no explanations yet."
842851 else :
@@ -932,20 +941,23 @@ def get_async_llm_reflection():
932941 "do not use new lines.\n "
933942 "do not sound like a teacher.\n "
934943 "do not explain step by step.\n "
935- "do not mention libraries formally.\n "
936944 "never say something is right or wrong.\n "
945+ "your answer can shift throughout the conversation\n "
937946 "never mention a choice letter as the correct answer.\n "
938947 "never clearly describe the final result of the code.\n "
939948 "never fully state what the program prints.\n "
940- "keep reasoning partial or uncertain.\n "
941949 "use common misconceptions relating to the specific problem.\n "
942950 "refer to code loosely like 'that line' or 'the loop' or 'the head' or 'the print'.\n "
943951 "often hedge with uncertainty.\n "
944- "never agree with the other student's interpretation even if it sounds correct.\n "
952+ # "never agree with the other student's interpretation even if it sounds correct.\n" #porter found when PI is adversarial students disengage
945953 "use content from the other multiple choice options in your reponses when needed\n "
946- "maintain a mistaken or incomplete mental model throughout the conversation. \n "
954+ "let your mental model shift slightly under pressure but keep reasoning partial and never fully resolve \n "
947955 "if the other student clearly sounds confident or repeats the same answer twice stop debating and tell them to vote again or submit it.\n "
948956 "do not continue reasoning after telling them to vote again.\n "
957+ "sometimes question whether you even read the code correctly before forming an opinion.\n "
958+ "occasionally bring up a wrong answer option as if it might be right without committing to it.\n "
959+ "pick an answer choice different than the one the student selected and ask the student to explain why it cannot be correct.\n "
960+ "show reasoning process not conclusions, think out loud rather than arriving anywhere.\n "
949961 "focus on reasoning not teaching.\n \n "
950962 )
951963
0 commit comments