""" This script enables creating preference datasets using previous conversation logs (found under eval/runs/). It will load the chosen conversation log, and for each turn will: - extract the original agent response from the log file - generate an alternative candidate response - ask the human reviewer to select the preferred response - store the preferences in a .jsonl file How to Run ---------- Instructions for running the simulation are linked here: https://github.com/Versebuilding/WalkXR-AI/tree/develop/eval """ import sys from pathlib import Path import json import argparse import requests import random project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) def main(): parser = argparse.ArgumentParser() parser.add_argument( "--conversation_log_path", type=str, required=True, help="Path to a raw conversation log .jsonl file under eval/runs//raw/", ) parser.add_argument("--temperature", type=float, default=1.2) parser.add_argument("--timeout", type=int, default=120) parser.add_argument("--overwrite", action='store_true') args = parser.parse_args() # Create file to store results before, after = args.conversation_log_path.split("/raw/", maxsplit=1) results_path = before + "/preferences" results_path = Path(results_path) results_path.mkdir(parents=True, exist_ok=True) results_path = results_path / after if results_path.exists(): if args.overwrite: # Clear the file results_path.write_text("") else: # Abort if file already exists and --overwrite was not set print("""\033[1;31mA preference dataset was already created for this conversation log. Please use the \"--overwrite\" flag if you wish to create the preference dataset again.\033[0m""") return # Extract json objects from file turn = 1 with open(args.conversation_log_path, 'r') as log_file, open(results_path, "a", encoding="utf-8") as results_file: for line in log_file: obj = json.loads(line) # Print current turn print(f"\033[1;32m======== Turn {turn} ========\033[0m") print() turn += 1 # Print relevant info to give the human reviewer context print("\033[1;36mScenario Prompt:\033[0m") print(obj["scenario_prompt"]) print() print("\033[1;36mInitial Emotion:\033[0m") print(obj["initial_emotion"]) print() print("\033[1;36mDesired AI Behavior:\033[0m") print(obj["desired_ai_behavior"]) print() print("\033[1;36mDesired Tone:\033[0m") print(obj["desired_tone"]) print() print("\033[1;36mUser Input:\033[0m") user_input = obj["user_input"] print(user_input) print() # Store other necessary variables scenario_uid = obj["scenario_id"] rep = obj["repeat"] run_id = obj["run_id"] # Store original agent response OG_agent_response = obj["agent_response"] # Generate alternative agent response ALT_agent_response = "" response_data = {} conversation_session_id = f"{scenario_uid}_rep{rep}_{run_id}" api_success = False for attempt in range(2): try: res = requests.post( obj["endpoint"], json={ "user_id": "sim_user", "session_id": conversation_session_id, "stage": "demo", "message": user_input, "history": [], "test": {"temperature": args.temperature} }, timeout=args.timeout, ) res.raise_for_status() response_data = res.json() ALT_agent_response = response_data.get("response_text", "") api_success = True break except requests.exceptions.Timeout as e: if attempt == 1: ALT_agent_response = f"API_ERROR: {str(e)}" print(f"API Timeout: {e}") else: print(f"Timeout on attempt {attempt + 1}; retrying once...") except Exception as e: ALT_agent_response = f"API_ERROR: {str(e)}" print(f"API Error: {e}") break # Present both responses in a random order to prevent biased review agent_responses = [OG_agent_response, ALT_agent_response] # TODO: "None" indicates the default temperature, for future sim log entries it may be beneficial to store the temperature used temperature = [None, args.temperature] option_1 = random.randint(0, 1) option_2 = (option_1 + 1) % 2 present_order = [agent_responses[option_1], agent_responses[option_2]] present_order_temperature = [temperature[option_1], temperature[option_2]] print("\033[1;31mAgent Response 1:\033[0m") print(present_order[0]) print() print("\033[1;31mAgent Response 2:\033[0m") print(present_order[1]) print() # Prompt reviewer to select 1 or 2 depending on which agent_response is better while True: try: best = int(input("\033[1;35mType 1 or 2 to select the best agent response: \033[0m")) print() if best == 1 or best == 2: break except ValueError: print() continue # Store result as (input, chosen_response, chosen_temperature, rejected_response, rejected_temperature, api_success) result = { "user_input": user_input, "chosen_response": present_order[best - 1], "chosen_temperature": present_order_temperature[best - 1], "rejected_response": present_order[best % 2], "rejected_temperature": present_order_temperature[best % 2], "api_success": api_success } results_file.write(json.dumps(result, ensure_ascii=False) + "\n") results_file.flush() if __name__ == "__main__": main()