modified: activity_yaml_validator.py

modified:   app.py
	modified:   research/activity29-battleship.yaml
	modified:   research/activity29-testship.yaml
	modified:   research/guarded_ai.py
	modified:   tests/functional/test_activity_flows.py
	modified:   tests/functional/test_battleship_pre_script.py
	modified:   tests/functional/test_guarded_ai.py
	modified:   tests/unit/test_activity_yaml_validator.py
	modified:   tests/unit/test_app_feedback.py
	modified:   tests/unit/test_guarded_ai.py
This commit is contained in:
Russell Ballestrini 2025-08-11 12:39:42 -04:00
parent d4d697db59
commit f90df2ae57
11 changed files with 573 additions and 286 deletions

View file

@ -205,6 +205,7 @@ sections:
- ai_shot
- user_hit_result
- ai_hit_result
- user_response
- name: "Ship Status"
tokens_for_ai: |
@ -214,12 +215,17 @@ sections:
- user_sunk_ship_this_round: If this contains a ship name like "Cruiser", it means THE USER destroyed an ENEMY ship
- ai_sunk_ship_this_round: If this contains a ship name like "Destroyer", it means THE ENEMY destroyed a USER ship
Your responses:
- If user_sunk_ship_this_round has a ship name: "💥 You have destroyed the enemy's [ship name]! It sinks beneath the waves!"
- If ai_sunk_ship_this_round has a ship name: "🔥 The enemy has destroyed your [ship name]! It has been claimed by the sea!"
- If both have ship names: combine both messages above
- If both are null/empty: "STFU"
Examples of when to respond:
- If ai_sunk_ship_this_round = "Submarine": Generate submarine destruction story
- If ai_sunk_ship_this_round = "Carrier": Generate carrier destruction story
- If user_sunk_ship_this_round = "Destroyer": Generate destroyer victory story
- If both = "None": Respond with "STFU"
Your responses:
- If user_sunk_ship_this_round equals "Carrier", "Battleship", "Cruiser", "Submarine", or "Destroyer": "💥 You have destroyed the enemy's [ship name]! Write 3 dramatic sentences describing how this specific type of warship meets its end - does it explode? Break apart? Burn? Implode? Make it cinematic!"
- If ai_sunk_ship_this_round equals "Carrier", "Battleship", "Cruiser", "Submarine", or "Destroyer": "🔥 The enemy has destroyed your [ship name]! Write 3 dramatic sentences describing how this specific type of warship is destroyed - the fire, water, explosions, or structural failure. Make it epic!"
- If both equal ship names: combine both messages above
- If both equal "None" or null: "STFU"
Do NOT confuse who destroyed what. user_sunk_ship_this_round = USER victory. ai_sunk_ship_this_round = USER loss.
metadata_filter:
- user_sunk_ship_this_round

View file

@ -183,6 +183,7 @@ sections:
- ai_shot
- user_hit_result
- ai_hit_result
- user_response
- name: "Ship Status"
tokens_for_ai: |
@ -192,12 +193,17 @@ sections:
- user_sunk_ship_this_round: If this contains a ship name like "Cruiser", it means THE USER destroyed an ENEMY ship
- ai_sunk_ship_this_round: If this contains a ship name like "Destroyer", it means THE ENEMY destroyed a USER ship
Your responses:
- If user_sunk_ship_this_round has a ship name: "💥 You have destroyed the enemy's [ship name]! It sinks beneath the waves!"
- If ai_sunk_ship_this_round has a ship name: "🔥 The enemy has destroyed your [ship name]! It has been claimed by the sea!"
- If both have ship names: combine both messages above
- If both are null/empty: "STFU"
Examples of when to respond:
- If ai_sunk_ship_this_round = "Submarine": Generate submarine destruction story
- If ai_sunk_ship_this_round = "Carrier": Generate carrier destruction story
- If user_sunk_ship_this_round = "Destroyer": Generate destroyer victory story
- If both = "None": Respond with "STFU"
Your responses:
- If user_sunk_ship_this_round equals "Carrier", "Battleship", "Cruiser", "Submarine", or "Destroyer": "💥 You have destroyed the enemy's [ship name]! Write 3 dramatic sentences describing how this specific type of warship meets its end - does it explode? Break apart? Burn? Implode? Make it cinematic!"
- If ai_sunk_ship_this_round equals "Carrier", "Battleship", "Cruiser", "Submarine", or "Destroyer": "🔥 The enemy has destroyed your [ship name]! Write 3 dramatic sentences describing how this specific type of warship is destroyed - the fire, water, explosions, or structural failure. Make it epic!"
- If both equal ship names: combine both messages above
- If both equal "None" or null: "STFU"
Do NOT confuse who destroyed what. user_sunk_ship_this_round = USER victory. ai_sunk_ship_this_round = USER loss.
metadata_filter:
- user_sunk_ship_this_round

View file

@ -163,39 +163,52 @@ def provide_feedback_prompts(
):
"""Generate feedback from multiple prompts"""
feedback_messages = []
# Add user_response to metadata for filtering purposes
full_metadata = metadata.copy()
full_metadata["user_response"] = user_response
for prompt in feedback_prompts:
prompt_name = prompt.get("name", "unnamed")
tokens_for_ai = prompt.get("tokens_for_ai", "")
# Apply per-prompt metadata filtering if specified
prompt_metadata = metadata
prompt_metadata = full_metadata
if "metadata_filter" in prompt:
filter_keys = prompt["metadata_filter"]
prompt_metadata = {k: v for k, v in metadata.items() if k in filter_keys}
prompt_metadata = {
k: v for k, v in full_metadata.items() if k in filter_keys
}
# Combine legacy tokens with prompt-specific tokens
if legacy_tokens_for_ai:
tokens_for_ai = legacy_tokens_for_ai + " " + tokens_for_ai
# Add language instruction
tokens_for_ai += f" Provide the feedback in {user_language}."
# Add transition-specific AI feedback if present
if "ai_feedback" in transition:
tokens_for_ai += f" {transition['ai_feedback'].get('tokens_for_ai', '')}"
# Determine user_response for this prompt based on metadata filtering
filtered_user_response = user_response
if (
"metadata_filter" in prompt
and "user_response" not in prompt["metadata_filter"]
):
filtered_user_response = "" # Remove user response if not in filter
ai_feedback = generate_ai_feedback(
category, question, user_response, tokens_for_ai, prompt_metadata
category, question, filtered_user_response, tokens_for_ai, prompt_metadata
)
# Only add feedback if it has content and isn't exactly the STFU token
if ai_feedback and ai_feedback.strip() and ai_feedback.strip() != "STFU":
feedback_messages.append({
"name": prompt_name,
"content": ai_feedback.strip()
})
feedback_messages.append(
{"name": prompt_name, "content": ai_feedback.strip()}
)
return feedback_messages
@ -463,7 +476,7 @@ def simulate_activity(yaml_file_path):
# Provide feedback based on the category
feedback_messages = []
if "feedback_prompts" in step:
# New multi-prompt system - legacy tokens get combined with each prompt
multi_feedback_messages = provide_feedback_prompts(
@ -474,7 +487,9 @@ def simulate_activity(yaml_file_path):
user_response,
user_language,
metadata,
step.get("feedback_tokens_for_ai", "") # Pass legacy tokens to be combined
step.get(
"feedback_tokens_for_ai", ""
), # Pass legacy tokens to be combined
)
feedback_messages.extend(multi_feedback_messages)
elif step.get("feedback_tokens_for_ai"):
@ -489,11 +504,8 @@ def simulate_activity(yaml_file_path):
metadata,
)
if feedback and feedback.strip():
feedback_messages.append({
"name": "Feedback",
"content": feedback
})
feedback_messages.append({"name": "Feedback", "content": feedback})
# Display all feedback messages
for feedback_msg in feedback_messages:
print(f"\n{feedback_msg['name']}: {feedback_msg['content']}")