@@ -25,6 +25,7 @@ class SkillResult:
2525 message : str
2626 data : Optional [Any ] = None
2727 error : Optional [str ] = None
28+ blocks : Optional [list ] = None
2829
2930
3031class SkillExecutor :
@@ -85,10 +86,17 @@ async def execute(
8586 # Generic LLM-based execution
8687 result = await self ._execute_with_llm (skill , text , params , user_id , thread_history )
8788
89+ # Skill handlers can return a dict with "message" + "blocks" for rich responses
90+ blocks = None
91+ if isinstance (result , dict ) and "message" in result :
92+ blocks = result .get ("blocks" )
93+ result = result ["message" ]
94+
8895 return SkillResult (
8996 success = True ,
9097 message = result ,
91- data = params
98+ data = params ,
99+ blocks = blocks
92100 )
93101
94102 except Exception as e :
@@ -341,7 +349,10 @@ async def _execute_medhack(
341349 "Do NOT reveal the correct diagnosis or hint at it."
342350 )
343351 guess_warning = f"\n \n _You have *{ remaining } * guess{ 'es' if remaining != 1 else '' } remaining._"
344- return llm_response + guess_warning
352+ return self ._medhack_game_response (
353+ llm_response + guess_warning ,
354+ current_case .get ("image_url" , "" )
355+ )
345356
346357 # --- Game interaction (not a guess) ---
347358 if is_game_q and current_case :
@@ -368,7 +379,8 @@ async def _execute_medhack(
368379 )
369380
370381 case_data = client .get_case_for_llm (today )
371- return await self ._medhack_llm_response (skill , text , case_data , thread_history )
382+ llm_response = await self ._medhack_llm_response (skill , text , case_data , thread_history )
383+ return self ._medhack_game_response (llm_response , current_case .get ("image_url" , "" ))
372384
373385 if is_game_q and not current_case :
374386 return (
@@ -387,7 +399,8 @@ async def _execute_medhack(
387399 "so you can no longer interact with it. Come back tomorrow for a new one!"
388400 )
389401 case_data = client .get_case_for_llm (today )
390- return await self ._medhack_llm_response (skill , text , case_data , thread_history )
402+ llm_response = await self ._medhack_llm_response (skill , text , case_data , thread_history )
403+ return self ._medhack_game_response (llm_response , current_case .get ("image_url" , "" ))
391404
392405 # --- Event info mode ---
393406 if is_event_q or (not is_game_q ):
@@ -420,6 +433,25 @@ async def _execute_medhack(
420433
421434 return await self ._execute_with_llm (skill , text , params , user_id , thread_history )
422435
436+ def _medhack_game_response (self , text : str , image_url : str = "" ) -> dict | str :
437+ """Wrap a game response with image blocks when the case has an image_url."""
438+ if image_url :
439+ return {
440+ "message" : text ,
441+ "blocks" : [
442+ {
443+ "type" : "image" ,
444+ "image_url" : image_url ,
445+ "alt_text" : "Patient case image" ,
446+ },
447+ {
448+ "type" : "section" ,
449+ "text" : {"type" : "mrkdwn" , "text" : text },
450+ },
451+ ],
452+ }
453+ return text
454+
423455 async def _medhack_llm_response (
424456 self ,
425457 skill : Skill ,
0 commit comments