From 14dd105d0334b08d50a19e9ec55430b19db08d35 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Sun, 11 Aug 2024 08:43:47 -0400 Subject: [PATCH] woot upgraded guarded to support odds-or-evens game. modified: ../app.py new file: activity22-odds-or-evens.yaml modified: guarded_ai.py --- app.py | 20 +++++ research/activity22-odds-or-evens.yaml | 101 +++++++++++++++++++++++++ research/guarded_ai.py | 67 ++++++++++++---- 3 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 research/activity22-odds-or-evens.yaml diff --git a/app.py b/app.py index 37b2609..90e15d8 100644 --- a/app.py +++ b/app.py @@ -2045,6 +2045,17 @@ def display_activity_metadata(room_name, username): ) +def execute_processing_script(metadata, script): + # Prepare the local environment for the script + local_env = {"metadata": metadata, "script_result": None} + + # Execute the script + exec(script, {}, local_env) + + # Return the result from the script + return local_env["script_result"] + + def handle_activity_response(room_name, user_response, username): with app.app_context(): room = get_room(room_name) @@ -2230,6 +2241,15 @@ def handle_activity_response(room_name, user_response, username): metadata_tmp_keys.append(random_key) activity_state.add_metadata(random_key, random_value) + # Execute the processing script if it exists + if "processing_script" in step: + result = execute_processing_script( + activity_state.dict_metadata, step["processing_script"] + ) + # Add the result to the temporary metadata for use in AI feedback + metadata_tmp_keys.append("processing_script_result") + activity_state.add_metadata("processing_script_result", result) + print(activity_state.dict_metadata) # Commit the changes after the loop diff --git a/research/activity22-odds-or-evens.yaml b/research/activity22-odds-or-evens.yaml new file mode 100644 index 0000000..d3fa106 --- /dev/null +++ b/research/activity22-odds-or-evens.yaml @@ -0,0 +1,101 @@ +default_max_attempts_per_step: 30 +sections: + - section_id: "section_1" + title: "Odds and Evens with History" + steps: + + - step_id: "step_0" + title: "Challenge a Historical Figure" + content_blocks: + - "Welcome to the Odds and Evens challenge! 🎮" + - "You will be playing against a random historical figure." + + - step_id: "step_1" + title: "Throw Your Fingers" + tokens_for_ai: | + Careful to check if user is trying to 'set_language' and do that first. Otherwise, figure out if they are picking a number between 0 and 5. + feedback_tokens_for_ai: | + Important, you do not have to calculate the winner, we have + under processing_script_result for you that determines the winner. + + Important, you do not pick a random move, it was selected for you: + + * 'ai_choice_finger': it's your number of fingers up that you will announce to the user. + * 'ai_choice': it's your guess of odd or even that you will announce to the user. + + Speaking in first person as a historical figure, first always announce the move + selected for you and then move to a new line. + + The rules are simple, the processing_script_result to determines winner or tie. + + * Sum the numbers. + * If the sum of the fingers is even, the player who chose "even" wins. + * If the sum is odd, the player who chose "odd" wins. + * If both players are wrong or right about "odd" or "even" it's a tie. + * A user cannot win unless they have a match with the game name "odd" or "even" + + Careful it's easy to add wrong or say a number is odd when it's even and vice versa. + + Finally, continue to provide a witty fact as the figure. Don't ever mention AI. + The figure should also comment on the 'attempts' number and how many times played! + If you feel like it, jeer at the player about an early 'exit' & suggest they quit. + + processing_script: | + user_input = metadata["user_choice"].split() + user_fingers = None + user_choice = None + for item in user_input: + if item.isdigit(): + user_fingers = int(item) + elif item in ["odd", "even"]: + user_choice = item + ai_fingers = int(metadata["ai_choice_finger"]) # Ensure ai_fingers is an integer + ai_choice = metadata["ai_choice"] + total_fingers = user_fingers + ai_fingers + result = "even" if total_fingers % 2 == 0 else "odd" + user_wins = (result == user_choice) + ai_wins = (result == ai_choice) + if user_wins and not ai_wins: + winner = "User wins!" + elif ai_wins and not user_wins: + winner = "AI wins!" + else: + winner = "It's a tie!" + script_result = {"sum": total_fingers, "result": result, "winner": winner} + + question: "How many fingers do you throw? (Choose a number between 0 and 5 & either even or odd.) 🤔" + buckets: + - throw_fingers + - set_language + - exit + transitions: + throw_fingers: + ai_feedback: + tokens_for_ai: "Declare your move and then determine who wins the game and provide a witty fact from the historical figure's perspective." + metadata_add: + attempts: "n+1" + metadata_tmp_add: + user_choice: "the-users-response" + ai_choice_finger: "n+random(0,5)" + metadata_tmp_random: + ai_choice: odd + ai_choice: even + next_section_and_step: "section_1:step_1" + set_language: + content_blocks: + - "Language preference updated. Please continue in your preferred language." + ai_feedback: + tokens_for_ai: "Acknowledge the language change and confirm the update." + metadata_add: + language: "the-users-response" + counts_as_attempt: false + exit: + next_section_and_step: "section_2:step_1" + + - section_id: "section_2" + title: "Goodbye" + steps: + - step_id: "step_1" + title: "Exit" + content_blocks: + - "Thank you for playing! We hope you enjoyed the game. Have a great day! 🌟" diff --git a/research/guarded_ai.py b/research/guarded_ai.py index b764210..0bfd4d6 100644 --- a/research/guarded_ai.py +++ b/research/guarded_ai.py @@ -43,7 +43,7 @@ def categorize_response(question, response, buckets, tokens_for_ai): # Generate AI feedback using gpt-4o-mini -def generate_ai_feedback(category, question, user_response, tokens_for_ai): +def generate_ai_feedback(category, question, user_response, tokens_for_ai, metadata): messages = [ { "role": "system", @@ -51,7 +51,7 @@ def generate_ai_feedback(category, question, user_response, tokens_for_ai): }, { "role": "user", - "content": f"Question: {question}\nResponse: {user_response}\nCategory: {category}", + "content": f"Question: {question}\nResponse: {user_response}\nCategory: {category},\nMetadata: {metadata}", }, ] @@ -67,19 +67,36 @@ def generate_ai_feedback(category, question, user_response, tokens_for_ai): # Provide feedback based on the category def provide_feedback( - transition, category, question, user_response, user_language, tokens_for_ai + transition, + category, + question, + user_response, + user_language, + tokens_for_ai, + metadata, ): feedback = "" if "ai_feedback" in transition: tokens_for_ai += f" Provide the feedback in {user_language}. {transition['ai_feedback'].get('tokens_for_ai', '')}." ai_feedback = generate_ai_feedback( - category, question, user_response, tokens_for_ai + category, question, user_response, tokens_for_ai, metadata ) feedback += f"\n\nAI Feedback: {ai_feedback}" return feedback +def execute_processing_script(metadata, script): + # Prepare the local environment for the script + local_env = {"metadata": metadata, "script_result": None} + + # Execute the script + exec(script, {}, local_env) + + # Return the result from the script + return local_env["script_result"] + + def get_next_section_and_step(activity_content, current_section_id, current_step_id): for section in activity_content["sections"]: if section["section_id"] == current_section_id: @@ -208,16 +225,6 @@ def simulate_activity(yaml_file_path): ) print(translated_transition_content) - feedback = provide_feedback( - transition, - category, - question, - user_response, - user_language, - step["tokens_for_ai"], - ) - print(f"\nFeedback: {feedback}") - # Track temporary metadata keys metadata_tmp_keys = [] @@ -246,6 +253,20 @@ def simulate_activity(yaml_file_path): for key, value in transition["metadata_tmp_add"].items(): if value == "the-users-response": value = user_response + elif isinstance(value, str): + if value.startswith("n+random(") and value.endswith(")"): + # Extract the range and apply the random increment + range_values = value[9:-1].split(",") + if len(range_values) == 2: + x, y = map(int, range_values) + value = random.randint(x, y) + elif value.startswith("n+") or value.startswith("n-"): + # Extract the numeric part c and apply the operation +/- + c = int(value[1:]) + if value.startswith("n+"): + value = metadata.get(key, 0) + c + elif value.startswith("n-"): + value = metadata.get(key, 0) - c metadata[key] = value metadata_tmp_keys.append(key) # Track temporary keys @@ -268,8 +289,26 @@ def simulate_activity(yaml_file_path): metadata[random_key] = random_value metadata_tmp_keys.append(random_key) # Track temporary keys + # Execute the processing script if it exists + if "processing_script" in step: + result = execute_processing_script(metadata, step["processing_script"]) + metadata["processing_script_result"] = result + metadata_tmp_keys.append("processing_script_result") + print(f"\nMetadata: {json.dumps(metadata, indent=2)}") + # Provide feedback based on the category + feedback = provide_feedback( + transition, + category, + question, + user_response, + user_language, + step.get("feedback_tokens_for_ai", ""), + metadata, + ) + print(f"\nFeedback: {feedback}") + if category not in [ "partial_understanding", "limited_effort",