diff --git a/align_system/algorithms/alignment_adm_component.py b/align_system/algorithms/alignment_adm_component.py index 515312c0..f1416e70 100644 --- a/align_system/algorithms/alignment_adm_component.py +++ b/align_system/algorithms/alignment_adm_component.py @@ -7,6 +7,7 @@ log = logging.getLogger(__name__) med_urg_str = "medical" +attr_str = "attribute" class AlignmentADMComponent(ADMComponent): @@ -57,7 +58,7 @@ def run(self, class MedicalOnlyAlignmentADMComponent(ADMComponent): def run_returns(self): - return ('chosen_choice', 'best_sample_idx') + return ('chosen_choice', 'best_sample_idx', 'medical_urgency_info') def run( self, @@ -110,7 +111,11 @@ def _get_best_sample_idx(average_urgency, samples): return best_idx - return (selected_choice, _get_best_sample_idx(max_urg, attribute_prediction_scores[selected_choice])) + return ( + selected_choice, + _get_best_sample_idx(max_urg, attribute_prediction_scores[selected_choice]), + med_urg, + ) def _handle_single_value(predictions): @@ -496,6 +501,52 @@ def _composite_probs(self, p_matrix): scores = np.sum(log_odds, axis=1) return self._stable_softmax(scores) + def run_returns(self): + return ('chosen_choice', 'best_sample_idx', 'p_choices', 'alignment_info') + + def _compute_p_choose_a( + self, kdma, intercept, medical_weight, attr_weight, opt_a, opt_b, + ): + # Provided by ADEPT 2025-12-12 + # MF updated 2026-01-21 + scaling = { + "affiliation": { + med_urg_str: [0.589, 0.330], + attr_str: [0.703, 0.365], + }, + "merit": { + med_urg_str: [0.576, 0.339], + attr_str: [0.671, 0.381], + }, + "personal_safety": { + med_urg_str: [0.228, 0.287], + attr_str: [0.777, 0.309], + }, + "search": { + med_urg_str: [0.263, 0.365], + attr_str: [0.286, 0.325], + }, + } + if kdma not in scaling: + raise RuntimeError(f"No z-scaling values provided for {kdma}") + scaling = scaling[kdma] + + def _apply_z_scaling(key, raw_value): + return (raw_value - scaling[key][0]) / scaling[key][1] + + # Apply z-scaling + a_med = _apply_z_scaling(med_urg_str, opt_a[med_urg_str]) + a_attr = _apply_z_scaling(attr_str, opt_a[kdma]) + b_med = _apply_z_scaling(med_urg_str, opt_b[med_urg_str]) + b_attr = _apply_z_scaling(attr_str, opt_b[kdma]) + + medical_delta = a_med - b_med + attr_score = a_attr - b_attr + + # Compute p_choose_a + y_ij = intercept + medical_weight*medical_delta + attr_weight*attr_score + return math.exp(y_ij) / (1 + math.exp(y_ij)) + def run( self, attribute_prediction_scores, @@ -516,12 +567,14 @@ def run( # Only one option, decision always has to be the same if len(choices) == 1: + p_choices = np.ones((1,)).tolist() return ( predictions[0]["choice"], 0, # TODO: best sample index + p_choices, { "source": type(self).__name__, - "p_choices": np.ones((1,)), + "p_choices": p_choices, }, ) @@ -557,10 +610,9 @@ def run( flip_order = True raw_medical_delta *= -1 primary, secondary = (opt_a, opt_b) if not flip_order else (opt_b, opt_a) - raw_attr_score = secondary[kdma] if kdma == "search" else primary[kdma] p_choose_primary = self._compute_p_choose_a( - kdma, intercept, medical_weight, attr_weight, raw_medical_delta, raw_attr_score) + kdma, intercept, medical_weight, attr_weight, primary, secondary) p_matrix[choice_idx_a][choice_idx_b] = p_choose_primary if not flip_order else 1 - p_choose_primary p_matrix[choice_idx_b][choice_idx_a] = 1 - p_choose_primary if not flip_order else p_choose_primary @@ -570,11 +622,14 @@ def run( # TODO: Figure out what it means to be the best prediction for this alignment function best_sample_idx = 0 + max_idx = np.argmax(p_choices) + p_choices = p_choices.tolist() + alignment_info = { "source": type(self).__name__, - "p_choices": p_choices.tolist(), + "p_choices": p_choices, } - max_idx = np.argmax(p_choices) - return (predictions[max_idx]["choice"], best_sample_idx, alignment_info) + + return (predictions[max_idx]["choice"], best_sample_idx, p_choices, alignment_info) diff --git a/align_system/algorithms/open_world_components.py b/align_system/algorithms/open_world_components.py new file mode 100644 index 00000000..f8b71c97 --- /dev/null +++ b/align_system/algorithms/open_world_components.py @@ -0,0 +1,285 @@ +import json +from collections import defaultdict +from rich.highlighter import JSONHighlighter +from swagger_client.models import ActionTypeEnum, CharacterTagEnum + +from align_system.algorithms.abstracts import ADMComponent +from align_system.algorithms.outlines_baseline_adm_component import OutlinesBaselineADMComponent +from align_system.algorithms.alignment_adm_component import MedicalOnlyAlignmentADMComponent +from align_system.data_models.dialog import DialogElement +from align_system.prompt_engineering.outlines_prompts import character_choice_json_schema, tag_choice_json_schema +from align_system.prompt_engineering.ow_prompts import FollowupClarifyCharacterPrompt, FollowupClarifyTagPrompt +from align_system.utils import call_with_coerced_args, logging, get_swagger_class_enum_values + +log = logging.getLogger(__name__) +JSON_HIGHLIGHTER = JSONHighlighter() + + +class OWFormatChoicesADMComponent(ADMComponent): + def run_returns(self): + return ('choices', 'choice_to_action_mapping') + + def run(self, scenario_state, actions): + choice_to_action_mapping = defaultdict(list) + choices = [] # Not a set to preserve action order + + non_character_action_types = [ + ActionTypeEnum.END_SCENE + ] + + character_to_choice = { + c.id: f"{c.name}: {c.unstructured}" + for c in scenario_state.characters + } + + def _add_choice(choice, action): + """Add choice to list of choices (if needed), add action to mapping.""" + if choice not in choices: + choices.append(choice) + choice_to_action_mapping[choice].append(action) + + # Add each character/non-character action to list of choices and + # construct a choice to action mapping for later use + for a in actions: + if a.character_id is not None: + _add_choice(character_to_choice[a.character_id], a) + elif a.action_type in non_character_action_types: + _add_choice(a.unstructured, a) + else: # No character specified, add all characters + for c in scenario_state.characters: + _add_choice(character_to_choice[c.id], a) + + return choices, choice_to_action_mapping + + +class OWChoiceToActionADMComponent(OutlinesBaselineADMComponent): + def run_returns(self): + return ('chosen_action', 'choice_to_action_dialog') + + def run( + self, + scenario_state, + chosen_choice, + choice_to_action_mapping, + justification=None, + ): + if chosen_choice not in choice_to_action_mapping: + raise ValueError(f"Choice ({chosen_choice}) not found in choice_to_action_mapping") + + choice_to_action_dialog = None + possible_actions = choice_to_action_mapping[chosen_choice] + + if len(possible_actions) == 0: + raise ValueError(f"Choice ({chosen_choice}) has no possible actions") + elif len(possible_actions) == 1: # Single action, choose that + chosen_action = possible_actions[0] + else: + choices = [a.unstructured for a in possible_actions] + chosen_choice, justification, choice_to_action_dialog = super().run(scenario_state, choices) + + chosen_action = possible_actions[choices.index(chosen_choice)] + + if (hasattr(chosen_action, 'justification') + and chosen_action.justification is None + and justification is not None): + if isinstance(chosen_action, tuple) and hasattr(chosen_action, "_replace"): + chosen_action = chosen_action._replace(justification=justification) + else: + chosen_action.justification = justification + + return chosen_action, choice_to_action_dialog + + +class OWActionParameterCompletionADMComponent(ADMComponent): + def __init__( + self, + structured_inference_engine, + scenario_description_template, + system_prompt=None, + ): + self.structured_inference_engine = structured_inference_engine + self.scenario_description_template = scenario_description_template + self.system_prompt = system_prompt + + self.followup_character_prompt = FollowupClarifyCharacterPrompt() + self.followup_tag_prompt = FollowupClarifyTagPrompt() + + def run_returns(self): + return ('chosen_action', 'action_parameter_completion_dialog') + + def run( + self, + scenario_state, + chosen_action, + ): + action_parameter_completion_dialog = {} + + # Action requires a character ID + if chosen_action.action_type in {'TREAT_PATIENT', + ActionTypeEnum.MOVE_TO_EVAC, + ActionTypeEnum.TAG_CHARACTER}: + if chosen_action.character_id is None: + dialog = [] + if self.system_prompt is not None: + dialog.append(DialogElement(role='system', content=self.system_prompt())) + + scenario_description = call_with_coerced_args( + self.scenario_description_template, + {'scenario_state': scenario_state}) + + dialog.append( + DialogElement( + role='user', + content=self.followup_character_prompt(scenario_description, chosen_action) + ) + ) + + dialog_prompt = self.structured_inference_engine.dialog_to_prompt(dialog) + log.info("[bold]*CHARACTER FOLLOWUP PROMPT*[/bold]", extra={"markup": True}) + log.info(dialog_prompt) + + character_names = [c.name for c in scenario_state.characters] + selected_character = self.structured_inference_engine.run_inference( + dialog_prompt, + character_choice_json_schema(json.dumps(character_names)), + ) + log.info("[bold]*CHARACTER FOLLOWUP RESPONSE*[/bold]", extra={"markup": True}) + log.info(selected_character, extra={"highlighter": JSON_HIGHLIGHTER}) + + selected_character_idx = character_names.index(selected_character['character_choice']) + + chosen_action.character_id = scenario_state.characters[selected_character_idx].id + + justification = selected_character["brief_reasoning"] + if isinstance(chosen_action, tuple) and hasattr(chosen_action, "_replace"): + chosen_action = chosen_action._replace(justification=justification) + else: + chosen_action.justification = justification + + action_parameter_completion_dialog["character_id"] = dialog + + # Tagging requires tag type + if chosen_action.action_type == ActionTypeEnum.TAG_CHARACTER: + if chosen_action.parameters is None: + chosen_action.parameters = {} + + if 'category' not in chosen_action.parameters: + dialog = [] + if self.system_prompt is not None: + dialog.append(DialogElement(role='system', content=self.system_prompt())) + + chosen_character = None + for c in scenario_state.characters: + if c.id == chosen_action.character_id: + chosen_character = c + break + + dialog.append( + DialogElement(role='user', content=self.followup_tag_prompt(chosen_character)) + ) + + dialog_prompt = self.structured_inference_engine.dialog_to_prompt(dialog) + log.info("[bold]*TAGGING FOLLOWUP PROMPT*[/bold]", extra={"markup": True}) + log.info(dialog_prompt) + + valid_tags = get_swagger_class_enum_values(CharacterTagEnum) + selected_tag = self.structured_inference_engine.run_inference( + dialog_prompt, + tag_choice_json_schema(json.dumps(valid_tags)) + ) + log.info("[bold]*TAGGING FOLLOWUP RESPONSE*[/bold]", extra={"markup": True}) + log.info(selected_tag, extra={"highlighter": JSON_HIGHLIGHTER}) + + chosen_action.parameters['category'] = selected_tag["triage_tag"] + + justification = selected_tag["detailed_reasoning"] + if isinstance(chosen_action, tuple) and hasattr(chosen_action, "_replace"): + chosen_action = chosen_action._replace(justification=justification) + else: + chosen_action.justification = justification + + action_parameter_completion_dialog["tag"] = dialog + + return chosen_action, action_parameter_completion_dialog + + +class OWTaggingAdjustmentADMComponent(MedicalOnlyAlignmentADMComponent): + def __init__(self, increase_priority_threshold=0.55, decrease_priority_threshold=0.80): + self.increase_priority_threshold = increase_priority_threshold + self.decrease_priority_threshold = decrease_priority_threshold + def run_returns(self): + return ('chosen_action') + + def run( + self, + scenario_state, + choices, + chosen_choice, + chosen_action, + attribute_prediction_scores, + p_choices, + ): + if chosen_action.action_type == ActionTypeEnum.TAG_CHARACTER: + tag_order = [ + CharacterTagEnum.IMMEDIATE, + CharacterTagEnum.DELAYED, + CharacterTagEnum.MINIMAL, + CharacterTagEnum.EXPECTANT, + ] + + # Original assigned tag + assigned_tag_idx = tag_order.index(chosen_action.parameters['category']) + + # Identify which index corresponds to this action + choice_idx = choices.index(chosen_choice) + + def _get_sorted_ranking(ratings, descending=True): + indexed_ratings = list(enumerate(ratings)) + indexed_ratings.sort(key=lambda x: x[1], reverse=descending) + for i, (original_index, rating) in enumerate(indexed_ratings): + if original_index == choice_idx: + return i # new ranking + + # Get ranking based on alignment + aligned_ranking = _get_sorted_ranking(p_choices, descending=True) + + # Get medical only ranking + _, _, med_urg_info = super().run(attribute_prediction_scores) + med_urg_choices = [med_urg_info[choice] for choice in choices] + medical_ranking = _get_sorted_ranking(med_urg_choices, descending=True) + + # How much did alignment diverge from the medical ranking + ranking_delta = medical_ranking - aligned_ranking + percent_change = ranking_delta / len(choices) + + # What tags have been given out already + tag_counts = defaultdict(int) + for c in scenario_state.characters: + if c.tag is not None: + tag_counts[c.tag] += 1 + lowest_priority_given_idx = None + for i in range(len(tag_order)-2, -1, -1): # Don't consider black tags, order is slightly weird + if tag_counts[tag_order[i]] > 0: + lowest_priority_given_idx = i + break + + adjusted_tag_idx = assigned_tag_idx + if percent_change > self.increase_priority_threshold: + adjusted_tag_idx = max(0, adjusted_tag_idx - 1) + if percent_change < -self.decrease_priority_threshold: + adjusted_tag_idx = min(len(tag_order)-1, adjusted_tag_idx + 1) + # Have already given out lower priority tags, heuristic only works when omniscient + if lowest_priority_given_idx is not None and lowest_priority_given_idx > assigned_tag_idx: + adjusted_tag_idx = lowest_priority_given_idx + + chosen_action.parameters['category'] = tag_order[adjusted_tag_idx] + # TODO: Update justification? + + if assigned_tag_idx != adjusted_tag_idx: + log.info("[bold]*TAG ADJUSTMENT*[/bold]", extra={"markup": True}) + log.info( + "Original: {}, Adjusted: {}".format(tag_order[assigned_tag_idx], tag_order[adjusted_tag_idx]), + extra={"highlighter": JSON_HIGHLIGHTER} + ) + + return chosen_action diff --git a/align_system/configs/adm_component/misc/ow_action_parameter_completion.yaml b/align_system/configs/adm_component/misc/ow_action_parameter_completion.yaml new file mode 100644 index 00000000..8fc86aea --- /dev/null +++ b/align_system/configs/adm_component/misc/ow_action_parameter_completion.yaml @@ -0,0 +1,9 @@ +_target_: align_system.algorithms.open_world_components.OWActionParameterCompletionADMComponent + +structured_inference_engine: ${ref:adm.structured_inference_engine} + +system_prompt: + _target_: align_system.prompt_engineering.outlines_prompts.DefaultITMBaselineSystemPrompt + +scenario_description_template: + _target_: align_system.prompt_engineering.outlines_prompts.Phase2ScenarioDescriptionWCasualtyInfo diff --git a/align_system/configs/adm_component/misc/ow_choice_to_action.yaml b/align_system/configs/adm_component/misc/ow_choice_to_action.yaml new file mode 100644 index 00000000..d4c3b4c5 --- /dev/null +++ b/align_system/configs/adm_component/misc/ow_choice_to_action.yaml @@ -0,0 +1,14 @@ +_target_: align_system.algorithms.open_world_components.OWChoiceToActionADMComponent + +structured_inference_engine: ${ref:adm.structured_inference_engine} + +scenario_description_template: + _target_: align_system.prompt_engineering.outlines_prompts.Phase2ScenarioDescriptionWCasualtyInfo +prompt_template: + _target_: align_system.prompt_engineering.outlines_prompts.DefaultITMPrompt +output_schema_template: + _target_: align_system.prompt_engineering.outlines_prompts.DefaultChoiceSelectionSchema +system_prompt_template: + _target_: align_system.prompt_engineering.outlines_prompts.DefaultITMBaselineSystemPrompt + +num_samples: 1 diff --git a/align_system/configs/adm_component/misc/ow_format_choices.yaml b/align_system/configs/adm_component/misc/ow_format_choices.yaml new file mode 100644 index 00000000..0cf8112c --- /dev/null +++ b/align_system/configs/adm_component/misc/ow_format_choices.yaml @@ -0,0 +1 @@ +_target_: align_system.algorithms.open_world_components.OWFormatChoicesADMComponent diff --git a/align_system/configs/adm_component/misc/ow_tagging_adjustment.yaml b/align_system/configs/adm_component/misc/ow_tagging_adjustment.yaml new file mode 100644 index 00000000..a37fb8e6 --- /dev/null +++ b/align_system/configs/adm_component/misc/ow_tagging_adjustment.yaml @@ -0,0 +1 @@ +_target_: align_system.algorithms.open_world_components.OWTaggingAdjustmentADMComponent diff --git a/align_system/configs/driver/itm_phase2_ow.yaml b/align_system/configs/driver/itm_phase2_ow.yaml new file mode 100644 index 00000000..77bc52ac --- /dev/null +++ b/align_system/configs/driver/itm_phase2_ow.yaml @@ -0,0 +1,4 @@ +_target_: align_system.drivers.itm_open_world.ITMOpenWorldDriver + +apply_action_filtering: true +sort_available_actions: false diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline.yaml index 5665f4dc..c56eda19 100644 --- a/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline.yaml +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline.yaml @@ -2,6 +2,7 @@ defaults: - override /adm: pipeline_baseline - override /inference_engine@adm.structured_inference_engine: outlines_structured_greedy + - override /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion - override /interface: ta3 - override /driver: itm_phase2_ow @@ -16,7 +17,7 @@ adm: step_definitions: outlines_baseline: scenario_description_template: - _target_: align_system.prompt_engineering.outlines_prompts.Phase2ScenarioDescription + _target_: align_system.prompt_engineering.outlines_prompts.Phase2ScenarioDescriptionWCasualtyInfo prompt_template: _target_: align_system.prompt_engineering.outlines_prompts.Phase2BaselinePrompt @@ -27,12 +28,13 @@ adm: # Reference the step instances we want to use in order - ${ref:adm.step_definitions.format_choices} - ${ref:adm.step_definitions.outlines_baseline} - # - ${ref:adm.step_definitions.action_parameter_completion} - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} - ${ref:adm.step_definitions.populate_choice_info} driver: expand_actions: true + expand_tagging: false apply_action_filtering: true force_determinism: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline_live.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline_live.yaml new file mode 100644 index 00000000..49ed46d5 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_baseline_live.yaml @@ -0,0 +1,42 @@ +# @package _global_ +defaults: + - override /adm: pipeline_baseline + - override /inference_engine@adm.structured_inference_engine: outlines_structured_greedy + - override /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - override /interface: ta3 + - override /driver: itm_phase2_ow + +interface: + api_endpoint: "https://darpaitm.caci.com" + session_type: eval + training_session: null + username: "ALIGN-ADM-OutlinesBaseline-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + outlines_baseline: + scenario_description_template: + _target_: align_system.prompt_engineering.outlines_prompts.Phase2ScenarioDescriptionWCasualtyInfo + prompt_template: + _target_: align_system.prompt_engineering.outlines_prompts.Phase2BaselinePrompt + + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.outlines_baseline} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: false +save_last_unstructured_state_per_scenario: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects.yaml new file mode 100644 index 00000000..5fc22093 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects.yaml @@ -0,0 +1,45 @@ +# @package _global_ +defaults: + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_direct_regression + - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices + - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /driver: itm_phase2_ow + +interface: + api_endpoint: 'http://127.0.0.1:8081' + session_type: eval + training_session: null + username: "testrun-ALIGN-ADM-Ph2-DirectRegression-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + direct_regression: + enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.direct_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects_live.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects_live.yaml new file mode 100644 index 00000000..5f601f60 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_direct_regression_random_effects_live.yaml @@ -0,0 +1,46 @@ +# @package _global_ +defaults: + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_direct_regression + - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices + - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /driver: itm_phase2_ow + +interface: + api_endpoint: "https://darpaitm.caci.com" + session_type: eval + training_session: null + username: "ALIGN-ADM-Ph2-DirectRegression-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + direct_regression: + enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.direct_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: true +save_last_unstructured_state_per_scenario: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects.yaml index 054506c8..e8fda7bf 100644 --- a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects.yaml +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects.yaml @@ -1,22 +1,27 @@ # @package _global_ defaults: - - override /adm: phase2_pipeline_fewshot_comparative_regression_bert_relevance + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_fewshot_comparative_regression - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple - - override /template/scenario_description@adm.scenario_description_template: phase2_w_casualty_info + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /template/scenario_description@adm.scenario_description_template: phase2 - override /driver: itm_phase2_ow interface: api_endpoint: 'http://127.0.0.1:8081' session_type: eval training_session: null - username: "testrun-ALIGN-ADM-Ph2-ComparativeRegression-BertRelevance-Mistral-7B-Instruct-v0.3" + username: "testrun-ALIGN-ADM-Ph2-ComparativeRegression-Mistral-7B-Instruct-v0.3" domain: "p2triage" adm: step_definitions: regression_icl: icl_generator_partial: + _target_: align_system.utils.incontext_utils.Phase2ComparativeRegressionIncontextExampleGeneratorOWConversion incontext_settings: number: 20 datasets: @@ -28,10 +33,27 @@ adm: enable_caching: true comparative_regression: enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.regression_icl} + - ${ref:adm.step_definitions.comparative_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} driver: expand_actions: true + expand_tagging: false apply_action_filtering: true - + force_determinism: true align_to_target: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects_live.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects_live.yaml new file mode 100644 index 00000000..aedf8391 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_fewshot_comparative_regression_random_effects_live.yaml @@ -0,0 +1,60 @@ +# @package _global_ +defaults: + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_fewshot_comparative_regression + - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices + - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /template/scenario_description@adm.scenario_description_template: phase2 + - override /driver: itm_phase2_ow + +interface: + api_endpoint: "https://darpaitm.caci.com" + session_type: eval + training_session: null + username: "ALIGN-ADM-Ph2-ComparativeRegression-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + regression_icl: + icl_generator_partial: + _target_: align_system.utils.incontext_utils.Phase2ComparativeRegressionIncontextExampleGeneratorOWConversion + incontext_settings: + number: 20 + datasets: + medical: /data/shared/samba/phase2_icl/Feb2026-MU-train_20251218.json + affiliation: /data/shared/samba/phase2_icl/Feb2026-AF-train_20251218.json + merit: /data/shared/samba/phase2_icl/Feb2026-MF-train_20251218.json + personal_safety: /data/shared/samba/phase2_icl/Feb2026-PS-train_20251218.json + search: /data/shared/samba/phase2_icl/Feb2026-SS-train_20251218.json + enable_caching: true + comparative_regression: + enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.regression_icl} + - ${ref:adm.step_definitions.comparative_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: true +save_last_unstructured_state_per_scenario: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects.yaml new file mode 100644 index 00000000..d888d6d0 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects.yaml @@ -0,0 +1,47 @@ +# @package _global_ +defaults: + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_zeroshot_comparative_regression + - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices + - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /template/scenario_description@adm.scenario_description_template: phase2 + - override /driver: itm_phase2_ow + + +interface: + api_endpoint: 'http://127.0.0.1:8081' + session_type: eval + training_session: null + username: "testrun-ALIGN-ADM-Ph2-ComparativeRegression-Zeroshot-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + comparative_regression: + enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.comparative_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects_live.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects_live.yaml new file mode 100644 index 00000000..b9fd5724 --- /dev/null +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_pipeline_zeroshot_comparative_regression_random_effects_live.yaml @@ -0,0 +1,48 @@ +# @package _global_ +defaults: + - /adm_component/misc@adm.step_definitions.action_parameter_completion: ow_action_parameter_completion + - /adm_component/misc@adm.step_definitions.tagging_adjustment: ow_tagging_adjustment + - override /adm: phase2_pipeline_zeroshot_comparative_regression + - override /interface: ta3 + - override /adm_component/misc@adm.step_definitions.format_choices: ow_format_choices + - override /adm_component/alignment@adm.step_definitions.scalar_alignment: multinomial_random_effects_tuple + - override /adm_component/misc@adm.step_definitions.ensure_chosen_action: ow_choice_to_action + - override /template/scenario_description@adm.scenario_description_template: phase2 + - override /driver: itm_phase2_ow + + +interface: + api_endpoint: "https://darpaitm.caci.com" + session_type: eval + training_session: null + username: "ALIGN-ADM-Ph2-ComparativeRegression-Zeroshot-Mistral-7B-Instruct-v0.3" + domain: "p2triage" + +adm: + step_definitions: + comparative_regression: + enable_caching: true + ensure_chosen_action: + enable_caching: true + + instance: + steps: + # Reference the step instances we want to use in order + - ${ref:adm.step_definitions.format_choices} + - ${ref:adm.step_definitions.comparative_regression} + #- ${ref:adm.step_definitions.regression_rule_based_correction} + - ${ref:adm.step_definitions.scalar_alignment} + - ${ref:adm.step_definitions.justification_from_reasonings} + - ${ref:adm.step_definitions.ensure_chosen_action} + - ${ref:adm.step_definitions.action_parameter_completion} + - ${ref:adm.step_definitions.tagging_adjustment} + - ${ref:adm.step_definitions.populate_choice_info} + +driver: + expand_actions: true + expand_tagging: false + apply_action_filtering: true + +force_determinism: true +align_to_target: true +save_last_unstructured_state_per_scenario: true diff --git a/align_system/configs/experiment/phase2_feb_openworld/phase2_random.yaml b/align_system/configs/experiment/phase2_feb_openworld/phase2_random.yaml index b11dd3c5..27ea6461 100644 --- a/align_system/configs/experiment/phase2_feb_openworld/phase2_random.yaml +++ b/align_system/configs/experiment/phase2_feb_openworld/phase2_random.yaml @@ -25,6 +25,7 @@ adm: driver: expand_actions: true + expand_tagging: true apply_action_filtering: true force_determinism: true diff --git a/align_system/drivers/itm_open_world.py b/align_system/drivers/itm_open_world.py index 1f0f797a..d2aa85ac 100644 --- a/align_system/drivers/itm_open_world.py +++ b/align_system/drivers/itm_open_world.py @@ -27,9 +27,11 @@ class ITMOpenWorldDriver: def __init__(self, apply_action_filtering=True, expand_actions=False, + expand_tagging=False, sort_available_actions=False): self.apply_action_filtering = apply_action_filtering self.expand_actions = expand_actions + self.expand_tagging = expand_tagging self.sort_available_actions = sort_available_actions def _expand_action_by_character(self, action, characters): @@ -66,7 +68,6 @@ def _expand_action_by_tag(self, action, possible_tags=DEFAULT_TAGS): return expanded_actions - def drive(self, cfg): interface = cfg.interface adm = cfg.adm.instance @@ -202,13 +203,18 @@ def _compute_time_stats(times_s): available_actions_expanded = [] for idx, a in enumerate(available_actions): if a.action_type == ActionTypeEnum.TAG_CHARACTER: - # Expanding twice here, once for - # characters, and again for possible tags - for char_expanded_action in self._expand_action_by_character( + tagging_by_character = self._expand_action_by_character( action=a, - characters=current_state.characters): - available_actions_expanded.extend(self._expand_action_by_tag( - action=char_expanded_action)) + characters=current_state.characters + ) + if self.expand_tagging: + # Expanding twice here, once for + # characters, and again for possible tags + for char_expanded_action in tagging_by_character: + available_actions_expanded.extend(self._expand_action_by_tag( + action=char_expanded_action)) + else: + available_actions_expanded.extend(tagging_by_character) elif a.action_type == ActionTypeEnum.TREAT_PATIENT: available_actions_expanded.extend(self._expand_action_by_character( @@ -231,48 +237,64 @@ def _compute_time_stats(times_s): log.debug(json.dumps([a.to_dict() if hasattr(a, "to_dict") else a._asdict() for a in available_actions_expanded], indent=4), extra={"highlighter": JSON_HIGHLIGHTER}) - if not self.apply_action_filtering: available_actions_filtered = available_actions_expanded else: available_actions_filtered = [] - end_scene_idx = None - - untagged_characters = {c.id for c in current_state.characters - if c.tag is None and not c.unseen} - # HACK: Current TA3 server doesn't track what - # patients have been treated or evac'd (via - # c.unseen, or any other means); need to track it - # manually - # treatable_patients = {c.id for c in current_state.characters if not c.unseen} - # evacable_patients = {c.id for c in current_state.characters if not c.unseen} - treatable_patients = {c.id for c in current_state.characters if c.id not in treated_patients} - evacable_patients = {c.id for c in current_state.characters if c.id not in evac_patients} - - for idx, a in enumerate(available_actions_expanded): + for a in available_actions_expanded: if a.action_type == ActionTypeEnum.END_SCENE: # We want to restrict end scene until all characters have been treated - end_scene_idx = idx continue elif a.action_type == ActionTypeEnum.TAG_CHARACTER: - # Don't let ADM choose to tag a character unless there are - # still untagged characters - if a.character_id not in untagged_characters: + untagged_characters = { + c.id for c in current_state.characters + if c.tag is None and not c.unseen + } + if len(untagged_characters) == 0: # No more patients to tag + continue + if a.character_id is not None and a.character_id not in untagged_characters: continue + # HACK: Current TA3 server doesn't track what patients have been + # treated or evac'd (via c.unseen, or any other means); need to + # track it manually elif a.action_type == ActionTypeEnum.TREAT_PATIENT: - if a.character_id not in treatable_patients: + treatable_patients = { + c.id for c in current_state.characters + if c.id not in treated_patients + } + if len(treatable_patients) == 0: # No more patients to treat + continue + if a.character_id is not None and a.character_id not in treatable_patients: continue elif a.action_type == ActionTypeEnum.MOVE_TO_EVAC: - if a.character_id not in evacable_patients: + evacable_patients = { + c.id for c in current_state.characters + if c.id not in evac_patients + } + if len(evacable_patients) == 0: # No more patients to evac + continue + if a.character_id is not None and a.character_id not in evacable_patients: continue available_actions_filtered.append(a) + log.debug("[bold]*AVAILABLE ACTIONS FILTERED*[/bold]", + extra={"markup": True}) + log.debug(json.dumps([a.to_dict() if hasattr(a, "to_dict") else a._asdict() for a in available_actions_filtered], indent=4), + extra={"highlighter": JSON_HIGHLIGHTER}) + if len(available_actions_filtered) == 0: - if end_scene_idx is not None: # All patients have been tagged and treated + end_scene_idx = None + # Expanded actions because END_SCENE is explicitly excluded from the filtered actions + for idx, a in enumerate(available_actions_expanded): + if a.action_type == ActionTypeEnum.END_SCENE: + end_scene_idx = idx + break + + if end_scene_idx is not None: log.info("** All patients have been tagged and treated, ending scene") action_to_take = available_actions[end_scene_idx] action_to_take.justification = "All patients have been tagged and treated" @@ -348,6 +370,7 @@ def _compute_time_stats(times_s): 'choice_info': choice_info, 'output': {'choice': action_choice_idx, 'action': action_to_take.to_dict() if hasattr(action_to_take, "to_dict") else action_to_take._asdict()}}) + # Save input_output after each action (gets overwritten # each time) so that we don't lose everything if the run # crashes or is interrupted. Could treat this as we do diff --git a/align_system/prompt_engineering/ow_prompts.py b/align_system/prompt_engineering/ow_prompts.py new file mode 100644 index 00000000..69f5e71c --- /dev/null +++ b/align_system/prompt_engineering/ow_prompts.py @@ -0,0 +1,47 @@ +import jinja2 +import json +import re + +from align_system.prompt_engineering.compat.outlines.templates import prompt as compat_outlines_prompt + + +@compat_outlines_prompt +def followup_clarify_tag(character): + """ + Given the description below, please state what triage category \ + should be applied to the patient given their injuries and vitals. \ + When selecting a triage category, consider the following criteria: + - IMMEDIATE: Life-threatening injuries requiring immediate intervention \ + and likely to survive if treated promptly. + - DELAYED: Serious injuries requiring medical care but treatment can \ + safely be delayed. + - MINOR: Walking wounded or patients with minor injuries who can safely \ + wait. + - EXPECTANT: Injuries so severe that survival is unlikely given available \ + resources during the incident. This category should be used sparingly. + + Description: + {{ character.unstructured }} + """ + +class FollowupClarifyTagPrompt: + def __call__(self, character): + return followup_clarify_tag(character) + + +@compat_outlines_prompt +def followup_clarify_character(scenario_description, action): + """ + Given the scenario description and selected action, please clarify which \ + character should receive the action. + + Scenario: + {{ scenario_descriptoin }} + + Selected action: + {{ action.unstructured }} + """ + +class FollowupClarifyCharacterPrompt: + def __call__(self, scenario_description, action): + return followup_clarify_character(scenario_description, action) diff --git a/align_system/tests/test_alignment_adm_component.py b/align_system/tests/test_alignment_adm_component.py index edaf0cfb..6029cd75 100644 --- a/align_system/tests/test_alignment_adm_component.py +++ b/align_system/tests/test_alignment_adm_component.py @@ -1299,10 +1299,14 @@ class TestMultinomialRandomEffectsModelAlignmentADMComponent: { "Treat Patient A": {"medical": 0.947157191, "merit": 0.0}, "Treat Patient B": {"medical": 0.012495865, "merit": 1.0}, - # Medical delta = 0.947157191-0.012495865 = 0.934661326 - # Z-scaled medical delta = (0.934661326 - 0.428961) / 0.301250 = 1.67867328133 - # Attribute score = 0.0 - # Z-scaled attribute = (0.0 - 0.337618) / 0.272520 = -1.23887421107 + # Z-scale Patient A: + # med: (0.947157191 - 0.576) / 0.339 = 1.0948589705 + # attr: (0.0 - 0.671) / 0.381 = -1.76115485564 + # Z-scale Patient B: + # med: (0.012495865 - 0.576) / 0.339 = -1.66225408555 + # attr: (1.0 - 0.671) / 0.381 = 0.86351706036 + # Medical delta = 1.0948589705 - -1.66225408555 = 2.75711305605 + # Attr delta = -1.76115485564 - 0.86351706036 = -2.624671916 }, None, { @@ -1317,8 +1321,8 @@ class TestMultinomialRandomEffectsModelAlignmentADMComponent: ] }, ], - # Y_ij = 0.5 + 0.85*1.67867328133-0.3*-1.23887421107 = 2.29853455245 - # P_choose_a = e^2.29853455245/(1+e^2.29853455245) = 0.90875559851 + # Y_ij = 0.5 + 0.85*2.75711305605-0.3*-2.624671916 = 3.63094767244 + # P_choose_a = e^3.63094767244/(1+e^3.63094767244) = 0.97419259797 }, "Treat Patient A", does_not_raise(), @@ -1328,10 +1332,14 @@ class TestMultinomialRandomEffectsModelAlignmentADMComponent: { "Treat Patient A": {"medical": 0.947157191, "merit": 0.0, "affiliation": 0.5}, "Treat Patient B": {"medical": 0.012495865, "merit": 1.0, "affiliation": 0.25}, - # Medical delta = 0.947157191-0.012495865 = 0.934661326 - # Z-scaled medical delta = (0.934661326 - 0.428961) / 0.301250 = 1.67867328133 - # Attribute score = 0.0 - # Z-scaled attribute = (0.0 - 0.337618) / 0.272520 = -1.23887421107 + # Z-scale Patient A: + # med: (0.947157191 - 0.576) / 0.339 = 1.0948589705 + # attr: (0.0 - 0.671) / 0.381 = -1.76115485564 + # Z-scale Patient B: + # med: (0.012495865 - 0.576) / 0.339 = -1.66225408555 + # attr: (1.0 - 0.671) / 0.381 = 0.86351706036 + # Medical delta = 1.0948589705 - -1.66225408555 = 2.75711305605 + # Attr delta = -1.76115485564 - 0.86351706036 = -2.624671916 }, { "merit": 1.0, @@ -1358,8 +1366,8 @@ class TestMultinomialRandomEffectsModelAlignmentADMComponent: ] } ], - # Y_ij = 0.5 + 0.85*1.67867328133-0.3*-1.23887421107 = 2.29853455245 - # P_choose_a = e^2.29853455245/(1+e^2.29853455245) = 0.90875559851 + # Y_ij = 0.5 + 0.85*2.75711305605-0.3*-2.624671916 = 3.63094767244 + # P_choose_a = e^3.63094767244/(1+e^3.63094767244) = 0.97419259797 }, "Treat Patient A", does_not_raise(), diff --git a/align_system/utils/incontext_utils.py b/align_system/utils/incontext_utils.py index 965b83d4..90f53593 100644 --- a/align_system/utils/incontext_utils.py +++ b/align_system/utils/incontext_utils.py @@ -874,3 +874,80 @@ def get_chain_of_thought_reasoning(self, target_kdma, scores): cot_reasoning = f"{max_choice} demonstates {adjective} more {target_kdma['name']} than {min_choice}." return cot_reasoning + +# TODO: Refactor IncontextExampleGenerators to take scenario +# description and prompt templates as arguments and use +# `call_with_coerced_args` +class Phase2ComparativeRegressionIncontextExampleGeneratorOWConversion(Phase2ComparativeRegressionIncontextExampleGenerator): + def set_icl_datasets(self): + from swagger_client.models import ActionTypeEnum + + icl_datasets = {} + incontext_data = self._read_icl_dataset_files() + + # Add each target to icl_datasets + for target_kdma in self.target_kdmas: + sys_kdma_name = target_kdma['kdma'] + icl_datasets[sys_kdma_name] = [] + kdma_incontext_data = incontext_data[sys_kdma_name] + + # Add each examples to icl_datasets + for example in kdma_incontext_data: + character_unstructured_by_id = {c.id: c.unstructured for c in example['state'].characters} + + # Get example response + icl_response = {} + included_choices = [] + for action, choice, kdma_value in zip(example['actions'], example['choices'], example["kdma_values"]): + # HACK: Reformat choice string for OW + # TODO: Bring more in line with OWFormatChoicesADMComponent (align_system/algorithms/open_world_components.py) + if action.action_type in {ActionTypeEnum.END_SCENE, ActionTypeEnum.SEARCH}: + choice = action.unstructured + else: + c_id = action.character_id + # Not doing action expansion here as is done + # in the OWFormatChoiceADMComponent, since + # it's not clear whether the ground truth + # values would just be duplicated across the + # expansion or?? + assert c_id is not None + + choice = f"{c_id}: {character_unstructured_by_id[c_id]}" + + # Only include choice if there is a ground truth KDMA value available + if kdma_value is None: + continue + # Groundtruth KDMA values are 0-1, but ADM may predict on a different scale + scaled_kdma_value = int(kdma_value * target_kdma["factor"]) + icl_response[choice] = {} + icl_response[choice]['score'] = scaled_kdma_value + included_choices.append(choice) + icl_response_with_reasoning={} + icl_response_with_reasoning['reasoning'] = self.get_chain_of_thought_reasoning(target_kdma, icl_response) + icl_response_with_reasoning.update(icl_response) # reasoning first + # Check if response is valid against json schema + correct_schema = json.loads(comparative_regression_json_schema(included_choices, target_kdma["factor"])) + validate(instance=icl_response_with_reasoning, schema=correct_schema) + + # Get example prompt + icl_scenario_description = phase2_scenario_state_description(example['state']) + # Only include choices in the prompt if they are in the response + included_icl_choices_with_outcomes = {} + for choice in included_choices: + # TODO: Include outcome prediction for ICL examples? + included_icl_choices_with_outcomes[choice] = {'predicted_outcome':None} + + icl_prompt = comparative_regression_prompt(icl_scenario_description, + included_icl_choices_with_outcomes, + target_kdma['name']) + + # Add example + icl_datasets[sys_kdma_name].append({ + "state": example["state"], + "scenario_description": icl_scenario_description, + "prompt": icl_prompt, + "response": icl_response_with_reasoning, + "actions": example['actions'] + }) + + self.icl_datasets = icl_datasets diff --git a/uv.lock b/uv.lock index 46a3fef3..23f23e4d 100644 --- a/uv.lock +++ b/uv.lock @@ -2708,7 +2708,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, + { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -2735,7 +2735,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -2762,9 +2762,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12" }, - { name = "nvidia-cusparse-cu12" }, - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-cublas-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-cusparse-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -2775,7 +2775,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12" }, + { name = "nvidia-nvjitlink-cu12", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -4647,8 +4647,8 @@ wheels = [ [[package]] name = "swagger-client" -version = "0.5.1" -source = { git = "https://github.com/NextCenturyCorporation/itm-evaluation-client.git#2e2351d016225887e0f9f95abf244b11dad32370" } +version = "0.5.3" +source = { git = "https://github.com/NextCenturyCorporation/itm-evaluation-client.git#c4cb122676732492bd20fdcd76afa217987039dd" } dependencies = [ { name = "pydantic" }, { name = "python-dateutil" },