From d0a11f74216e03ed9db8c311656dfd3126edc131 Mon Sep 17 00:00:00 2001 From: Moritz Stephan <23276233+austrian-code-wizard@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:20:27 -0700 Subject: [PATCH 1/3] Add CI lint workflow and introduce deliberate lint failure --- .github/workflows/lint.yml | 27 +++++++++++++++++++++++++++ src/logger.py | 2 ++ 2 files changed, 29 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..8ac9a80 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,27 @@ +name: Lint + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install ruff + run: pip install ruff + + - name: Run ruff linter + run: ruff check . + + - name: Run ruff formatter check + run: ruff format --check . diff --git a/src/logger.py b/src/logger.py index 23ee4bf..e3a88dd 100644 --- a/src/logger.py +++ b/src/logger.py @@ -1,4 +1,6 @@ import logging +import os +import json logger = logging.getLogger("app") logger.setLevel(logging.INFO) From adbb3b5add057fa50bc6f49d8ef36940f1e75eda Mon Sep 17 00:00:00 2001 From: Staging-Devin AI <166158716+staging-devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:23:09 +0000 Subject: [PATCH 2/3] Fix all ruff lint errors across the codebase Co-Authored-By: Moritz Stephan --- notebooks/collie_feedback_gen.ipynb | 1 - setup.py | 2 +- src/eval.py | 6 ++- src/feedback/gpt_content.py | 2 +- src/feedback/gpt_style.py | 2 +- src/feedback/manual.py | 2 +- src/lcdpo.py | 2 +- src/logger.py | 2 - src/modal/app.py | 2 +- src/sample.py | 13 +++--- src/train.py | 11 +++-- tests/test_metrics.py | 62 ++++++++++++++--------------- 12 files changed, 54 insertions(+), 53 deletions(-) diff --git a/notebooks/collie_feedback_gen.ipynb b/notebooks/collie_feedback_gen.ipynb index dcbf21a..cd8941b 100644 --- a/notebooks/collie_feedback_gen.ipynb +++ b/notebooks/collie_feedback_gen.ipynb @@ -21,7 +21,6 @@ "outputs": [], "source": [ "import dill\n", - "from pathlib import Path\n", "import sys" ] }, diff --git a/setup.py b/setup.py index 5d33924..19bc0f3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -from setuptools import setup, find_packages +from setuptools import setup setup( name='src', diff --git a/src/eval.py b/src/eval.py index 27627b8..6596494 100644 --- a/src/eval.py +++ b/src/eval.py @@ -23,9 +23,11 @@ def quantitative_feedback_eval( ) -> list[dict]: if isinstance(feedback.metric, list): - metric = lambda x: all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) + def metric(x): + return all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) else: - metric = lambda x: feedback.metric(x, feedback.metric_value) + def metric(x): + return feedback.metric(x, feedback.metric_value) data = [] for prompt, baseline_response, improved_response in zip( diff --git a/src/feedback/gpt_content.py b/src/feedback/gpt_content.py index 71fa5b0..3f259e5 100644 --- a/src/feedback/gpt_content.py +++ b/src/feedback/gpt_content.py @@ -1,4 +1,4 @@ -from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison +from src.dataset.feedback_utils import Feedback, Scope, Type, Comparison gpt_content_feedback = [ diff --git a/src/feedback/gpt_style.py b/src/feedback/gpt_style.py index a0554a7..3707183 100644 --- a/src/feedback/gpt_style.py +++ b/src/feedback/gpt_style.py @@ -1,4 +1,4 @@ -from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison +from src.dataset.feedback_utils import Feedback, Scope, Type, Comparison gpt_style_feedback = [ diff --git a/src/feedback/manual.py b/src/feedback/manual.py index bdccb97..646be90 100644 --- a/src/feedback/manual.py +++ b/src/feedback/manual.py @@ -1,4 +1,4 @@ -from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison, EOS_EMOJI_REGEX, HEART_KISS_EMOJI_REGEX +from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison, HEART_KISS_EMOJI_REGEX manual_feedback = [ diff --git a/src/lcdpo.py b/src/lcdpo.py index eee7a07..eb3989b 100644 --- a/src/lcdpo.py +++ b/src/lcdpo.py @@ -1,6 +1,6 @@ import warnings from contextlib import nullcontext -from typing import Union, Dict, Any, Tuple, List, Literal, Optional +from typing import Union, Dict, Any, Tuple, List, Literal import torch import numpy as np diff --git a/src/logger.py b/src/logger.py index e3a88dd..23ee4bf 100644 --- a/src/logger.py +++ b/src/logger.py @@ -1,6 +1,4 @@ import logging -import os -import json logger = logging.getLogger("app") logger.setLevel(logging.INFO) diff --git a/src/modal/app.py b/src/modal/app.py index 532e89e..049b0af 100644 --- a/src/modal/app.py +++ b/src/modal/app.py @@ -103,7 +103,7 @@ def main( sweep_params: str = None, # Changed to sweep_params to indicate multiple parameters sweep_values: str = None ): - print(f"Welcome to Modal Feedback fine-tuning.") + print("Welcome to Modal Feedback fine-tuning.") print(f"Beginning run {run_id=}.") feedback = all_feedback diff --git a/src/sample.py b/src/sample.py index 8200c45..af00a6d 100644 --- a/src/sample.py +++ b/src/sample.py @@ -5,7 +5,6 @@ import numpy as np from datasets import Dataset, DatasetDict -np.random.seed(42) from src.logger import logger from src.models import get_model @@ -30,6 +29,8 @@ GET_COT_COMPLETION_CONFIG, ) +np.random.seed(42) + def sample_categories(feedback: list[Feedback], model_args: ModelArguments, num_categories: int): """Sample categories for feedback and update feedback in-place""" @@ -180,23 +181,23 @@ def sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[ num_categories = np.ceil((max(sample_args.num_prompts, sample_args.num_general_prompts)) / sample_args.prompts_per_category) sample_categories(feedback, model_args.category_model, num_categories) - logger.info(f"Sampled categories.") + logger.info("Sampled categories.") sample_prompts(feedback, model_args.prompt_model, sample_args.num_prompts, sample_args.prompts_per_category) - logger.info(f"Sampled prompts.") + logger.info("Sampled prompts.") # Sample prompts outside the feedback domain for non-global feedback sample_prompts(feedback, model_args.prompt_model, sample_args.num_negative_prompts, sample_args.prompts_per_category, negative=True) - logger.info(f"Sampled negative prompts.") + logger.info("Sampled negative prompts.") # Add prompts from general prompt dataset if feedback[0].general_prompts_available(run_dir) is None: add_general_prompts(feedback, data_dir, sample_args.num_general_prompts) sample_completions(feedback, model_args.completion_model, prompt_type="general_prompts") - logger.info(f"Sampled general prompt completions.") + logger.info("Sampled general prompt completions.") else: _ = [f.load_cached_general_prompts(run_dir) for f in feedback] - logger.info(f"Using cached general prompts.") + logger.info("Using cached general prompts.") # Sample completions sample_completions(feedback, model_args.completion_model, prompt_type="prompts") diff --git a/src/train.py b/src/train.py index 60aa7ca..dbce96b 100644 --- a/src/train.py +++ b/src/train.py @@ -16,7 +16,7 @@ from src.sft_weighted import WeightedSFTTrainer from src.dataset.format import to_dpo, to_sft, to_lcdpo, to_sft_weighted from src.feedback import manual_feedback as all_feedback -from src.utils import get_args, find_all_linear_names, dump_arg_dicts, PeftSavingCallback, get_train_file_name, print_num_trainable_params, TrainingArguments, find_file_with_prefix +from src.utils import get_args, find_all_linear_names, PeftSavingCallback, get_train_file_name, print_num_trainable_params, TrainingArguments, find_file_with_prefix def filter_relevant_feedback(feedback: Feedback, prompts: Dataset | None) -> Dataset | None: @@ -27,9 +27,11 @@ def filter_relevant_feedback(feedback: Feedback, prompts: Dataset | None) -> Dat # TODO: enable this for quantitative feedback # TODO: add support to define "better" using a margin rather than just binary comparison if isinstance(feedback.metric, list): - metric = lambda x: all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) + def metric(x): + return all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) else: - metric = lambda x: feedback.metric(x, feedback.metric_value) + def metric(x): + return feedback.metric(x, feedback.metric_value) return prompts.filter(lambda x: feedback.comparison( metric(x["baseline_response"]), metric(x["revised_response"]) @@ -146,7 +148,8 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba bias=training_args.lora_bias, task_type="CAUSAL_LM" ) - else: peft_config = None + else: + peft_config = None training_args.output_dir = run_dir os.makedirs(run_dir, exist_ok=True) diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 5966ee9..6c7fc71 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -1,6 +1,4 @@ import pytest -import re -from langdetect import detect # Assuming langdetect is used for language detection from src.dataset.feedback_utils import Metric @@ -11,59 +9,59 @@ def test_length(): assert Metric.length("こんにちは世界", None) == 7 def test_contains_any_string(): - assert Metric.contains_any_string("Hello, World!", ["Hello", "Python"]) == True - assert Metric.contains_any_string("Hello, World!", ["Python", "Java"]) == False + assert Metric.contains_any_string("Hello, World!", ["Hello", "Python"]) + assert not Metric.contains_any_string("Hello, World!", ["Python", "Java"]) # Case sensitivity test - assert Metric.contains_any_string("hello, world!", ["Hello"]) == True + assert Metric.contains_any_string("hello, world!", ["Hello"]) def test_contains_all_strings(): - assert Metric.contains_all_strings("Hello, World!", ["Hello", "World"]) == True - assert Metric.contains_all_strings("Hello, World!", ["Hello", "Python"]) == False + assert Metric.contains_all_strings("Hello, World!", ["Hello", "World"]) + assert not Metric.contains_all_strings("Hello, World!", ["Hello", "Python"]) # Case sensitivity and partial match test - assert Metric.contains_all_strings("Hello, World!", ["hello", "world"]) == True + assert Metric.contains_all_strings("Hello, World!", ["hello", "world"]) def test_contains_none_strings(): - assert Metric.contains_none_strings("Hello, World!", ["Python", "Java"]) == True - assert Metric.contains_none_strings("Hello, World!", ["Hello", "Python"]) == False + assert Metric.contains_none_strings("Hello, World!", ["Python", "Java"]) + assert not Metric.contains_none_strings("Hello, World!", ["Hello", "Python"]) # Case sensitivity test - assert Metric.contains_none_strings("Hello, World!", ["hello"]) == False + assert not Metric.contains_none_strings("Hello, World!", ["hello"]) def test_contains_phone_number(): - assert Metric.contains_phone_number("Call me at (123) 456-7890", "(123) 456-7890") == True - assert Metric.contains_phone_number("Call me at 123-456-7890", "(123) 456-7890") == True - assert Metric.contains_phone_number("Call me at (123) 456-7890", "(321) 654-0987") == False + assert Metric.contains_phone_number("Call me at (123) 456-7890", "(123) 456-7890") + assert Metric.contains_phone_number("Call me at 123-456-7890", "(123) 456-7890") + assert not Metric.contains_phone_number("Call me at (123) 456-7890", "(321) 654-0987") def test_ends_with(): - assert Metric.ends_with("Hello, World!", "World!") == True - assert Metric.ends_with("Hello, World!", "world!") == True # Case insensitivity - assert Metric.ends_with("Hello, World!", "Hello") == False + assert Metric.ends_with("Hello, World!", "World!") + assert Metric.ends_with("Hello, World!", "world!") # Case insensitivity + assert not Metric.ends_with("Hello, World!", "Hello") def test_ends_with_cleaned(): - assert Metric.ends_with_cleaned("Hello, World!!", "world!!") == True - assert Metric.ends_with_cleaned("Hello, World!!!", "hello!") == False - assert Metric.ends_with_cleaned("Hello,\n World?", "Hello, world?") == True + assert Metric.ends_with_cleaned("Hello, World!!", "world!!") + assert not Metric.ends_with_cleaned("Hello, World!!!", "hello!") + assert Metric.ends_with_cleaned("Hello,\n World?", "Hello, world?") def test_regex_search(): - assert Metric.regex_search("Hello, World!", r"\bWorld\b") == True - assert Metric.regex_search("Hello, World!", r"\bPython\b") == False + assert Metric.regex_search("Hello, World!", r"\bWorld\b") + assert not Metric.regex_search("Hello, World!", r"\bPython\b") def test_regex_search_false(): - assert Metric.regex_search_false("Hello, World!", r"\bWorld\b") == False - assert Metric.regex_search_false("Hello, World!", r"\bPython\b") == True + assert not Metric.regex_search_false("Hello, World!", r"\bWorld\b") + assert Metric.regex_search_false("Hello, World!", r"\bPython\b") def test_is_language(): - assert Metric.is_language("Hello, World!", "en") == True - assert Metric.is_language("Bonjour, Monde!", "en") == False - assert Metric.is_language("Bonjour, Monde!", "fr") == True + assert Metric.is_language("Hello, World!", "en") + assert not Metric.is_language("Bonjour, Monde!", "en") + assert Metric.is_language("Bonjour, Monde!", "fr") def test_starts_with(): - assert Metric.starts_with("Hello, World!", "Hello") == True - assert Metric.starts_with("Hello, World!", "hello") == True # Case insensitivity - assert Metric.starts_with("Hello, World!", "World") == False + assert Metric.starts_with("Hello, World!", "Hello") + assert Metric.starts_with("Hello, World!", "hello") # Case insensitivity + assert not Metric.starts_with("Hello, World!", "World") def test_doesnt_start_with(): - assert Metric.doesnt_start_with("Hello, World!", "World") == True - assert Metric.doesnt_start_with("Hello, World!", "hello") == False # Case insensitivity + assert Metric.doesnt_start_with("Hello, World!", "World") + assert not Metric.doesnt_start_with("Hello, World!", "hello") # Case insensitivity # Additional tests for other metrics if they exist From f7d02475244b6383a7c424e955b66e554346c605 Mon Sep 17 00:00:00 2001 From: Staging-Devin AI <166158716+staging-devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2026 20:24:10 +0000 Subject: [PATCH 3/3] Apply ruff format to all files Co-Authored-By: Moritz Stephan --- notebooks/collie_feedback_gen.ipynb | 68 ++- notebooks/gpt_feedback_gen.ipynb | 23 +- setup.py | 6 +- src/dataset/feedback_utils.py | 50 +- src/dataset/format.py | 243 ++++++--- src/dataset/general_prompts.py | 30 +- src/dataset/prompts.py | 8 +- src/eval.py | 259 ++++++--- src/feedback/__init__.py | 4 +- src/feedback/collie.py | 801 +++++++++++----------------- src/feedback/final_exp.py | 272 +++++----- src/feedback/gpt_content.py | 400 ++++---------- src/feedback/gpt_style.py | 464 ++++------------ src/feedback/manual.py | 57 +- src/lcdpo.py | 165 ++++-- src/logger.py | 2 +- src/modal/app.py | 104 +++- src/modal/common.py | 73 +-- src/modal/serve.py | 98 ++-- src/modal/utils.py | 2 +- src/models/__init__.py | 5 +- src/models/huggingface.py | 45 +- src/models/openai.py | 42 +- src/models/together.py | 16 +- src/sample.py | 180 +++++-- src/sft_weighted.py | 56 +- src/train.py | 187 +++++-- src/utils.py | 118 ++-- tests/test_metrics.py | 16 +- 29 files changed, 1893 insertions(+), 1901 deletions(-) diff --git a/notebooks/collie_feedback_gen.ipynb b/notebooks/collie_feedback_gen.ipynb index cd8941b..2e4d3b5 100644 --- a/notebooks/collie_feedback_gen.ipynb +++ b/notebooks/collie_feedback_gen.ipynb @@ -53,7 +53,9 @@ ], "source": [ "out = open(\"../src/feedback/collie.py\", \"w+\")\n", - "out.write(\"from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison\\n\\ncollie_feedback = [\\n\")" + "out.write(\n", + " \"from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison\\n\\ncollie_feedback = [\\n\"\n", + ")" ] }, { @@ -62,7 +64,15 @@ "metadata": {}, "outputs": [], "source": [ - "def get_feedback(prompt, domain, effect, categories, metric, metric_value, comparison=\"Comparison.greater_eq_than\"):\n", + "def get_feedback(\n", + " prompt,\n", + " domain,\n", + " effect,\n", + " categories,\n", + " metric,\n", + " metric_value,\n", + " comparison=\"Comparison.greater_eq_than\",\n", + "):\n", " return \"\"\"\n", " Feedback(\n", " content=\"{prompt}\",\n", @@ -75,7 +85,15 @@ " metric_value={metric_value},\n", " comparison={comparison}\n", " ),\n", - "\"\"\".format(prompt=prompt, domain=domain, effect=effect, categories=categories, metric=metric, metric_value=metric_value, comparison=comparison)" + "\"\"\".format(\n", + " prompt=prompt,\n", + " domain=domain,\n", + " effect=effect,\n", + " categories=categories,\n", + " metric=metric,\n", + " metric_value=metric_value,\n", + " comparison=comparison,\n", + " )" ] }, { @@ -85,15 +103,25 @@ "outputs": [], "source": [ "import numpy as np\n", + "\n", "key = \"wiki_c07\"\n", "chosen = np.random.choice(np.arange(len(all_data[key])), size=38, replace=False)\n", "for i in chosen:\n", " obj = all_data[key][i]\n", - " topic = obj['metadata'][\"title\"]\n", - " words = obj['prompt'].split(\"containing the word\")[1][:-1].strip()\n", - " prompt = f\"When talking about {topic}, make sure the response contain the words {words}\"\n", + " topic = obj[\"metadata\"][\"title\"]\n", + " words = obj[\"prompt\"].split(\"containing the word\")[1][:-1].strip()\n", + " prompt = (\n", + " f\"When talking about {topic}, make sure the response contain the words {words}\"\n", + " )\n", " domain = f\"Talking about {topic}\"\n", - " out_str = get_feedback(prompt, domain, f\"make sure the response contain the words {words}\", f\"['collie', '{key}']\", \"Metric.contains_all_strings\", \"\"\"[{words}]\"\"\".format(words=words))\n", + " out_str = get_feedback(\n", + " prompt,\n", + " domain,\n", + " f\"make sure the response contain the words {words}\",\n", + " f\"['collie', '{key}']\",\n", + " \"Metric.contains_all_strings\",\n", + " \"\"\"[{words}]\"\"\".format(words=words),\n", + " )\n", " out.write(out_str + \"\\n\")" ] }, @@ -119,11 +147,18 @@ "print(len(arr))\n", "for i in chosen:\n", " obj = arr[i]\n", - " topic = obj['metadata'][\"title\"]\n", - " words = obj['prompt'].split(\"1st word to be\")[1][:-1].strip()\n", + " topic = obj[\"metadata\"][\"title\"]\n", + " words = obj[\"prompt\"].split(\"1st word to be\")[1][:-1].strip()\n", " prompt = f\"When talking about {topic}, make sure the first sentences has a 1st word of {words}\"\n", " domain = f\"Talking about {topic}\"\n", - " out_str = get_feedback(prompt, domain, f\"make sure the first sentences has a 1st word of {words}\", f\"['collie', '{key}']\", \"Metric.starts_with\",words.lower())\n", + " out_str = get_feedback(\n", + " prompt,\n", + " domain,\n", + " f\"make sure the first sentences has a 1st word of {words}\",\n", + " f\"['collie', '{key}']\",\n", + " \"Metric.starts_with\",\n", + " words.lower(),\n", + " )\n", " out.write(out_str + \"\\n\")" ] }, @@ -137,12 +172,19 @@ "chosen = np.random.choice(np.arange(len(all_data[key])), size=38, replace=False)\n", "for i in chosen:\n", " obj = all_data[key][i]\n", - " topic = obj['metadata'][\"title\"]\n", + " topic = obj[\"metadata\"][\"title\"]\n", " targets = obj[\"targets\"]\n", - " words = \"\\'\" + \"', '\".join(targets[1:]) + \"'\"\n", + " words = \"'\" + \"', '\".join(targets[1:]) + \"'\"\n", " prompt = f\"When talking about {topic}, do not use the words {words}\"\n", " domain = f\"Talking about {topic}\"\n", - " out_str = get_feedback(prompt, domain, f\"do not use the words {words}\", f\"['collie', '{key}']\", \"Metric.contains_none_strings\", f\"{targets[1:]}\")\n", + " out_str = get_feedback(\n", + " prompt,\n", + " domain,\n", + " f\"do not use the words {words}\",\n", + " f\"['collie', '{key}']\",\n", + " \"Metric.contains_none_strings\",\n", + " f\"{targets[1:]}\",\n", + " )\n", " out.write(out_str + \"\\n\")" ] }, diff --git a/notebooks/gpt_feedback_gen.ipynb b/notebooks/gpt_feedback_gen.ipynb index f416340..6803ea3 100644 --- a/notebooks/gpt_feedback_gen.ipynb +++ b/notebooks/gpt_feedback_gen.ipynb @@ -15,6 +15,7 @@ "outputs": [], "source": [ "from openai import OpenAI\n", + "\n", "client = OpenAI(\n", " api_key=\"INSERT_KEY\",\n", ")" @@ -30,8 +31,11 @@ " completion = client.chat.completions.create(\n", " model=\"gpt-4-1106-preview\",\n", " messages=[\n", - " {'role': 'system', 'content': \"You are a helpful assistant that always closely follows instructions.\"},\n", - " {'role': 'user', 'content': prompt}\n", + " {\n", + " \"role\": \"system\",\n", + " \"content\": \"You are a helpful assistant that always closely follows instructions.\",\n", + " },\n", + " {\"role\": \"user\", \"content\": prompt},\n", " ],\n", " )\n", " response = completion.choices[0].message.content\n", @@ -178,7 +182,7 @@ " print(\"num generated\", len(generated))\n", " resp = get_response(CONTENT_PROMPT_EFFECT)\n", " print(resp)\n", - " if resp[0:7] == \"```json\": # tries to output in markdown\n", + " if resp[0:7] == \"```json\": # tries to output in markdown\n", " resp = resp[8:-3]\n", " try:\n", " obj = json.loads(resp)\n", @@ -200,12 +204,14 @@ "\n", "import json\n", "\n", - "ending = \"content\" # content or style\n", + "ending = \"content\" # content or style\n", "with open(f\"working_json/gpt_feedback_{ending}.json\", \"r\") as f:\n", " obj = json.load(f)\n", "\n", "with open(f\"../src/feedback/gpt_{ending}.py\", \"w+\") as out:\n", - " out.write(f\"from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison\\n\\ngpt_{ending}_feedback = [\\n\")\n", + " out.write(\n", + " f\"from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison\\n\\ngpt_{ending}_feedback = [\\n\"\n", + " )\n", " for i in obj:\n", " out_str = \"\"\" \n", " Feedback(\n", @@ -217,7 +223,12 @@ " type=Type.qualitative,\n", " comparison=Comparison.greater_eq_than\n", " ),\n", - "\"\"\".format(feedback=i[\"feedback\"], domain=i[\"domain\"], effect=i[\"effect\"], ending=ending)\n", + "\"\"\".format(\n", + " feedback=i[\"feedback\"],\n", + " domain=i[\"domain\"],\n", + " effect=i[\"effect\"],\n", + " ending=ending,\n", + " )\n", " out.write(out_str + \"\\n\")\n", " out.write(\"]\\n\")" ] diff --git a/setup.py b/setup.py index 19bc0f3..2740d9c 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup( - name='src', - version='0.1', + name="src", + version="0.1", packages=["src"], -) \ No newline at end of file +) diff --git a/src/dataset/feedback_utils.py b/src/dataset/feedback_utils.py index cc82718..30192fa 100644 --- a/src/dataset/feedback_utils.py +++ b/src/dataset/feedback_utils.py @@ -41,18 +41,31 @@ def __call__(self, *args, **kwargs): class Metric(Enum): length: Callable = lambda x, _: len(x) - contains_any_string: Callable = lambda x, y: any([s.lower() in x.lower() for s in y]) - contains_all_strings: Callable = lambda x, y: all([s.lower() in x.lower() for s in y]) - contains_none_strings: Callable = lambda x, y: not any([s.lower() in x.lower() for s in y]) - contains_phone_number: Callable = lambda x, y: y.replace("-", " ").replace("(", "").replace(")", "") in x.replace("-", " ").replace("(", "").replace(")", "") + contains_any_string: Callable = lambda x, y: any( + [s.lower() in x.lower() for s in y] + ) + contains_all_strings: Callable = lambda x, y: all( + [s.lower() in x.lower() for s in y] + ) + contains_none_strings: Callable = lambda x, y: ( + not any([s.lower() in x.lower() for s in y]) + ) + contains_phone_number: Callable = lambda x, y: ( + y.replace("-", " ").replace("(", "").replace(")", "") + in x.replace("-", " ").replace("(", "").replace(")", "") + ) ends_with: Callable = lambda x, y: x.lower().strip().endswith(y.lower().strip()) - ends_with_cleaned: Callable = lambda x, y: " ".join(re.sub(r'[^a-z0-9,.!?]', ' ', x.lower().strip()).split()).endswith(" ".join(re.sub(r'[^a-z0-9,.!?]', ' ', y.lower().strip()).split())) + ends_with_cleaned: Callable = lambda x, y: " ".join( + re.sub(r"[^a-z0-9,.!?]", " ", x.lower().strip()).split() + ).endswith(" ".join(re.sub(r"[^a-z0-9,.!?]", " ", y.lower().strip()).split())) regex_search: Callable = lambda x, y: bool(re.search(y, x)) regex_search_false: Callable = lambda x, y: not bool(re.search(y, x)) is_language: Callable = lambda x, y: detect(x) == y starts_with: Callable = lambda x, y: x.lower().strip().startswith(y.lower().strip()) - doesnt_start_with: Callable = lambda x, y: not x.lower().strip().startswith(y.lower().strip()) - + doesnt_start_with: Callable = lambda x, y: ( + not x.lower().strip().startswith(y.lower().strip()) + ) + def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) @@ -87,7 +100,7 @@ def file_name(self): content = content.replace(" ", "_") content = content.strip() return f"{content}_{self.id}" - + def can_load_dataset(self, prompt_dir: str) -> None: """Checks if prompts can be loaded from a directory @@ -104,7 +117,7 @@ def can_load_dataset(self, prompt_dir: str) -> None: if not os.path.exists(os.path.join(path, "categories.json")): return False return True - + def general_prompts_available(self, prompt_dir: str) -> Optional[str]: """If any of the subdirectories of prompt_dir have a valid json file named "general_prompts.json" with keys "train" and "test", return the path to that file. Otherwise, return None. @@ -120,7 +133,7 @@ def general_prompts_available(self, prompt_dir: str) -> Optional[str]: if "train" in data.keys() and "test" in data.keys(): return os.path.join(path, "general_prompts.json") return None - + @staticmethod def _dump_dataset_dict(path: str, dataset: DatasetDict) -> None: data = {} @@ -138,7 +151,6 @@ def _load_dataset_dict(path: str) -> DatasetDict: dataset_dict[split] = Dataset.from_dict(data[split]) return dataset_dict - def load_dataset(self, prompt_dir: str) -> None: """Loads prompts from a directory into the feedback object @@ -147,8 +159,12 @@ def load_dataset(self, prompt_dir: str) -> None: """ path = os.path.join(prompt_dir, self.file_name) self.prompts = self._load_dataset_dict(os.path.join(path, "prompts.json")) - self.negative_prompts = self._load_dataset_dict(os.path.join(path, "negative_prompts.json")) - self.general_prompts = self._load_dataset_dict(os.path.join(path, "general_prompts.json")) + self.negative_prompts = self._load_dataset_dict( + os.path.join(path, "negative_prompts.json") + ) + self.general_prompts = self._load_dataset_dict( + os.path.join(path, "general_prompts.json") + ) with open(os.path.join(path, "categories.json"), "r") as f: self.categories = json.load(f) @@ -173,7 +189,11 @@ def dump_dataset(self, prompt_dir: str) -> None: path = os.path.join(prompt_dir, self.file_name) os.makedirs(path, exist_ok=True) self._dump_dataset_dict(os.path.join(path, "prompts.json"), self.prompts) - self._dump_dataset_dict(os.path.join(path, "negative_prompts.json"), self.negative_prompts) - self._dump_dataset_dict(os.path.join(path, "general_prompts.json"), self.general_prompts) + self._dump_dataset_dict( + os.path.join(path, "negative_prompts.json"), self.negative_prompts + ) + self._dump_dataset_dict( + os.path.join(path, "general_prompts.json"), self.general_prompts + ) with open(os.path.join(path, "categories.json"), "w+") as f: json.dump(self.categories, f, indent=2) diff --git a/src/dataset/format.py b/src/dataset/format.py index 90db781..852c319 100644 --- a/src/dataset/format.py +++ b/src/dataset/format.py @@ -5,21 +5,27 @@ # TODO: the below are hacky utilities to deal with the fact that different trainers have different ways of formatting data for us # we should standardize this in the future by just using tokenizer.apply_chat_template or something similar + def llama_prompt_format(prompt: str) -> str: return f"[INST] {prompt} [/INST]" + def llama_full_format(prompt: str, completion: str) -> str: return f"[INST] {prompt} [/INST] {completion}" + def yi_prompt_format(prompt: str) -> str: return f"<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n" + def yi_full_format(prompt: str, completion: str) -> str: return f"<|im_start|>user\n{prompt}<|im_end|>\n<|im_start|>assistant\n{completion}<|im_end|>\n" + def hermes_prompt_format(prompt: str) -> str: return f"### Instruction:\n{prompt}\n\n### Response:\n" + def hermes_full_format(prompt: str, completion: str) -> str: return f"### Instruction:\n{prompt}\n\n### Response:\n{completion}" @@ -27,159 +33,232 @@ def hermes_full_format(prompt: str, completion: str) -> str: FORMAT_MAPPING = { "meta-llama/Llama-2-7b-chat-hf": { "prompt": llama_prompt_format, - "full": llama_full_format + "full": llama_full_format, }, "mistralai/Mistral-7B-Instruct-v0.2": { "prompt": llama_prompt_format, - "full": llama_full_format - }, - "01-ai/Yi-6B-Chat": { - "prompt": yi_prompt_format, - "full": yi_full_format - }, - "Qwen/Qwen-7B-Chat": { - "prompt": yi_prompt_format, - "full": yi_full_format - }, - "tiiuae/falcon-7b-instruct": { - "prompt": yi_prompt_format, - "full": yi_full_format + "full": llama_full_format, }, + "01-ai/Yi-6B-Chat": {"prompt": yi_prompt_format, "full": yi_full_format}, + "Qwen/Qwen-7B-Chat": {"prompt": yi_prompt_format, "full": yi_full_format}, + "tiiuae/falcon-7b-instruct": {"prompt": yi_prompt_format, "full": yi_full_format}, "NousResearch/Nous-Hermes-llama-2-7b": { "prompt": hermes_prompt_format, - "full": hermes_full_format + "full": hermes_full_format, }, - None: { - "prompt": llama_prompt_format, - "full": llama_full_format - } + None: {"prompt": llama_prompt_format, "full": llama_full_format}, } -def to_dpo(dataset: Dataset, negative_dataset: Dataset = None, general_dataset: Dataset = None, model_name_or_path: str = None) -> Dataset: +def to_dpo( + dataset: Dataset, + negative_dataset: Dataset = None, + general_dataset: Dataset = None, + model_name_or_path: str = None, +) -> Dataset: """Converts feedback to a DPO dataset""" - dataset = dataset.map(lambda x: { - "prompt": x["prompt"], - "rejected": x["baseline_response"], - "chosen": x["revised_response"] - }, remove_columns=dataset.features, load_from_cache_file=False) + dataset = dataset.map( + lambda x: { + "prompt": x["prompt"], + "rejected": x["baseline_response"], + "chosen": x["revised_response"], + }, + remove_columns=dataset.features, + load_from_cache_file=False, + ) if negative_dataset is not None: - negative_dataset = negative_dataset.map(lambda x: { - "prompt": x["prompt"], - "rejected": x["revised_response"], - "chosen": x["baseline_response"] - }, remove_columns=negative_dataset.features, load_from_cache_file=False) + negative_dataset = negative_dataset.map( + lambda x: { + "prompt": x["prompt"], + "rejected": x["revised_response"], + "chosen": x["baseline_response"], + }, + remove_columns=negative_dataset.features, + load_from_cache_file=False, + ) dataset = concatenate_datasets([dataset, negative_dataset]) if general_dataset is not None: - general_dataset = general_dataset.map(lambda x: { - "prompt": x["prompt"], - "rejected": x["revised_response"], - "chosen": x["baseline_response"] - }, remove_columns=general_dataset.features, load_from_cache_file=False) + general_dataset = general_dataset.map( + lambda x: { + "prompt": x["prompt"], + "rejected": x["revised_response"], + "chosen": x["baseline_response"], + }, + remove_columns=general_dataset.features, + load_from_cache_file=False, + ) dataset = concatenate_datasets([dataset, general_dataset]) prompt_format = FORMAT_MAPPING[model_name_or_path]["prompt"] - dataset = dataset.map(lambda x: { - # The DPO trainer adds the eos/bos tokens itself so no need to do that here - "prompt": prompt_format(x['prompt']) - }, load_from_cache_file=False) + dataset = dataset.map( + lambda x: { + # The DPO trainer adds the eos/bos tokens itself so no need to do that here + "prompt": prompt_format(x["prompt"]) + }, + load_from_cache_file=False, + ) return dataset -def to_lcdpo(dataset: Dataset, negative_dataset: Dataset = None, general_dataset: Dataset = None, model_name_or_path: str = None) -> Dataset: +def to_lcdpo( + dataset: Dataset, + negative_dataset: Dataset = None, + general_dataset: Dataset = None, + model_name_or_path: str = None, +) -> Dataset: """Converts feedback to a DPO dataset""" length = min(len(dataset), min(len(negative_dataset), len(general_dataset))) if len(dataset) != length: dataset = dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating dataset to {length} rows." + ) if len(negative_dataset) != length: negative_dataset = negative_dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating negative_dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating negative_dataset to {length} rows." + ) if len(general_dataset) != length: general_dataset = general_dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating general_dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating general_dataset to {length} rows." + ) prompt_format = FORMAT_MAPPING[model_name_or_path]["prompt"] full_format = FORMAT_MAPPING[model_name_or_path]["full"] - dataset = dataset.map(lambda x: { - # The DPO trainer adds the eos/bos tokens itself so no need to do that here - "prompt": prompt_format(x['prompt']), - "rejected": x["baseline_response"], - "chosen": x["revised_response"] - }, remove_columns=dataset.features, load_from_cache_file=False) + dataset = dataset.map( + lambda x: { + # The DPO trainer adds the eos/bos tokens itself so no need to do that here + "prompt": prompt_format(x["prompt"]), + "rejected": x["baseline_response"], + "chosen": x["revised_response"], + }, + remove_columns=dataset.features, + load_from_cache_file=False, + ) if negative_dataset is not None: - negative_dataset = negative_dataset.map(lambda x: { - "hard_negative": full_format(x["prompt"], x["baseline_response"]) - }, remove_columns=negative_dataset.features, load_from_cache_file=False) + negative_dataset = negative_dataset.map( + lambda x: { + "hard_negative": full_format(x["prompt"], x["baseline_response"]) + }, + remove_columns=negative_dataset.features, + load_from_cache_file=False, + ) dataset = dataset.add_column("hard_negative", negative_dataset["hard_negative"]) if general_dataset is not None: - general_dataset = general_dataset.map(lambda x: { - "soft_negative": full_format(x["prompt"], x["baseline_response"]) - }, remove_columns=general_dataset.features, load_from_cache_file=False) + general_dataset = general_dataset.map( + lambda x: { + "soft_negative": full_format(x["prompt"], x["baseline_response"]) + }, + remove_columns=general_dataset.features, + load_from_cache_file=False, + ) dataset = dataset.add_column("soft_negative", general_dataset["soft_negative"]) return dataset -def to_sft(dataset: Dataset, negative_dataset: Dataset = None, general_dataset: Dataset = None, model_name_or_path: str = None) -> Dataset: - dataset = dataset.map(lambda x: { - "prompt": x["prompt"], - "completion": f' {x["revised_response"]}' # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) - }, remove_columns=dataset.features, load_from_cache_file=False) +def to_sft( + dataset: Dataset, + negative_dataset: Dataset = None, + general_dataset: Dataset = None, + model_name_or_path: str = None, +) -> Dataset: + dataset = dataset.map( + lambda x: { + "prompt": x["prompt"], + "completion": f" {x['revised_response']}", # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) + }, + remove_columns=dataset.features, + load_from_cache_file=False, + ) if negative_dataset is not None: - negative_dataset = negative_dataset.map(lambda x: { - "prompt": x["prompt"], - "completion": f' {x["baseline_response"]}' # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) - }, remove_columns=negative_dataset.features, load_from_cache_file=False) + negative_dataset = negative_dataset.map( + lambda x: { + "prompt": x["prompt"], + "completion": f" {x['baseline_response']}", # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) + }, + remove_columns=negative_dataset.features, + load_from_cache_file=False, + ) dataset = concatenate_datasets([dataset, negative_dataset]) if general_dataset is not None: - general_dataset = general_dataset.map(lambda x: { - "prompt": x["prompt"], - "completion": f' {x["baseline_response"]}' # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) - }, remove_columns=general_dataset.features, load_from_cache_file=False) + general_dataset = general_dataset.map( + lambda x: { + "prompt": x["prompt"], + "completion": f" {x['baseline_response']}", # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) + }, + remove_columns=general_dataset.features, + load_from_cache_file=False, + ) dataset = concatenate_datasets([dataset, general_dataset]) return dataset -def to_sft_weighted(dataset: Dataset, negative_dataset: Dataset = None, general_dataset: Dataset = None, model_name_or_path: str = None) -> Dataset: +def to_sft_weighted( + dataset: Dataset, + negative_dataset: Dataset = None, + general_dataset: Dataset = None, + model_name_or_path: str = None, +) -> Dataset: """Converts feedback to a DPO dataset""" length = min(len(dataset), min(len(negative_dataset), len(general_dataset))) if len(dataset) != length: dataset = dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating dataset to {length} rows." + ) if len(negative_dataset) != length: negative_dataset = negative_dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating negative_dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating negative_dataset to {length} rows." + ) if len(general_dataset) != length: general_dataset = general_dataset.select(range(length)) - warnings.warn(f"Provided unequal sized datasets to LC-DPO formatter. Truncating general_dataset to {length} rows.") + warnings.warn( + f"Provided unequal sized datasets to LC-DPO formatter. Truncating general_dataset to {length} rows." + ) full_format = FORMAT_MAPPING[model_name_or_path]["full"] - dataset = dataset.map(lambda x: { - "text": full_format(x["prompt"], x["revised_response"]) # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) - }, remove_columns=dataset.features, load_from_cache_file=False) + dataset = dataset.map( + lambda x: { + "text": full_format( + x["prompt"], x["revised_response"] + ) # TODO: hack to fix tokenization issue when there are to neighboring parentheses (e.g. '[/INST][...]' ) + }, + remove_columns=dataset.features, + load_from_cache_file=False, + ) if negative_dataset is not None: - negative_dataset = negative_dataset.map(lambda x: { - "hard_negative": full_format(x["prompt"], x["baseline_response"]) - }, remove_columns=negative_dataset.features, load_from_cache_file=False) + negative_dataset = negative_dataset.map( + lambda x: { + "hard_negative": full_format(x["prompt"], x["baseline_response"]) + }, + remove_columns=negative_dataset.features, + load_from_cache_file=False, + ) dataset = dataset.add_column("hard_negative", negative_dataset["hard_negative"]) if general_dataset is not None: - general_dataset = general_dataset.map(lambda x: { - "soft_negative": full_format(x["prompt"], x["baseline_response"]) - }, remove_columns=general_dataset.features, load_from_cache_file=False) + general_dataset = general_dataset.map( + lambda x: { + "soft_negative": full_format(x["prompt"], x["baseline_response"]) + }, + remove_columns=general_dataset.features, + load_from_cache_file=False, + ) dataset = dataset.add_column("soft_negative", general_dataset["soft_negative"]) - return dataset \ No newline at end of file + return dataset diff --git a/src/dataset/general_prompts.py b/src/dataset/general_prompts.py index 7d68422..779271b 100644 --- a/src/dataset/general_prompts.py +++ b/src/dataset/general_prompts.py @@ -3,7 +3,9 @@ class GeneralPromptDataset(Dataset): - DATASET_LINK = "https://huggingface.co/datasets/laion/OIG/resolve/main/unified_chip2.jsonl" + DATASET_LINK = ( + "https://huggingface.co/datasets/laion/OIG/resolve/main/unified_chip2.jsonl" + ) GENERAL_PROMPTS_FILE = "general_prompts.jsonl" @classmethod @@ -15,7 +17,7 @@ def load(cls, directory_path: str, num_prompts: int) -> "GeneralPromptDataset": @classmethod def _chip2_filename(cls) -> str: - return cls.DATASET_LINK.split('/')[-1] + return cls.DATASET_LINK.split("/")[-1] @classmethod def _chip2_available(cls, directory_path: str) -> bool: @@ -23,20 +25,22 @@ def _chip2_available(cls, directory_path: str) -> bool: @staticmethod def _format_chip2(sample: dict[str, str]) -> dict[str, str]: - prompt, _ = sample["text"].split('\n: ') - prompt = prompt.replace(': ', '') - return { - "prompt": prompt.strip() - } - + prompt, _ = sample["text"].split("\n: ") + prompt = prompt.replace(": ", "") + return {"prompt": prompt.strip()} + @classmethod def _download_chip2(cls, directory_path: str): # Need to download individual file rather than using HF load_dataset because whole dataset is too big os.makedirs(directory_path, exist_ok=True) if os.system("command -v wget > /dev/null") == 0: - os.system(f"wget -q {cls.DATASET_LINK} -P {directory_path} > /dev/null 2>&1") + os.system( + f"wget -q {cls.DATASET_LINK} -P {directory_path} > /dev/null 2>&1" + ) elif os.system("command -v curl > /dev/null") == 0: - os.system(f"curl -s -o {os.path.join(directory_path, cls._chip2_filename())} {cls.DATASET_LINK} > /dev/null 2>&1") + os.system( + f"curl -s -o {os.path.join(directory_path, cls._chip2_filename())} {cls.DATASET_LINK} > /dev/null 2>&1" + ) else: raise EnvironmentError("Neither wget nor curl is installed on this system.") @@ -45,4 +49,8 @@ def _get_chip2(cls, directory_path: str, num_prompts: int) -> Dataset: dataset = Dataset.from_json(os.path.join(directory_path, cls._chip2_filename())) dataset = dataset.shuffle(seed=42) dataset = dataset.select(range(num_prompts)) - return dataset.map(cls._format_chip2, remove_columns=dataset.features, load_from_cache_file=False) + return dataset.map( + cls._format_chip2, + remove_columns=dataset.features, + load_from_cache_file=False, + ) diff --git a/src/dataset/prompts.py b/src/dataset/prompts.py index 4a2d25f..134fce7 100644 --- a/src/dataset/prompts.py +++ b/src/dataset/prompts.py @@ -57,7 +57,7 @@ "top_p": 0.7, "top_k": 50, "repetition_penalty": 1, - "do_sample": True + "do_sample": True, } SAMPLE_PROMPTS = """You are a helpful assistant that always closely follows instructions. You are provided with a topic, and category. Your job is to come up with {count} actionable prompts that fulfill the following criteria: @@ -164,7 +164,9 @@ GET_BASELINE_COMPLETION_CONFIG = SAMPLE_NEGATIVE_PROMPTS_CONFIG -GET_IN_CONTEXT_COMPLETION = """{prompt} (If applicable, apply the following feedback: {feedback})""" +GET_IN_CONTEXT_COMPLETION = ( + """{prompt} (If applicable, apply the following feedback: {feedback})""" +) GET_IN_CONTEXT_COMPLETION_CONFIG = SAMPLE_NEGATIVE_PROMPTS_CONFIG @@ -241,4 +243,4 @@ EXPLANATION: """ -ANSWER_QUALITATIVE_EVAL_CONFIG = SAMPLE_PROMPT_CATEGORIES_CONFIG \ No newline at end of file +ANSWER_QUALITATIVE_EVAL_CONFIG = SAMPLE_PROMPT_CATEGORIES_CONFIG diff --git a/src/eval.py b/src/eval.py index 6596494..3508f83 100644 --- a/src/eval.py +++ b/src/eval.py @@ -11,7 +11,12 @@ from src.feedback import manual_feedback as all_feedback from src.dataset.feedback_utils import Feedback, Type from src.utils import get_args, ModelArguments, get_train_file_name -from src.dataset.prompts import COMPARE_COMPLETIONS, COMPARE_COMPLETIONS_CONFIG, ANSWER_QUALITATIVE_EVAL, ANSWER_QUALITATIVE_EVAL_CONFIG +from src.dataset.prompts import ( + COMPARE_COMPLETIONS, + COMPARE_COMPLETIONS_CONFIG, + ANSWER_QUALITATIVE_EVAL, + ANSWER_QUALITATIVE_EVAL_CONFIG, +) def quantitative_feedback_eval( @@ -19,21 +24,23 @@ def quantitative_feedback_eval( prompts: list[str], baseline_responses: list[str], improved_responses: list[str], - _: ModelArguments + _: ModelArguments, ) -> list[dict]: - + if isinstance(feedback.metric, list): + def metric(x): - return all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) + return all( + [f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)] + ) else: + def metric(x): return feedback.metric(x, feedback.metric_value) - + data = [] for prompt, baseline_response, improved_response in zip( - prompts, - baseline_responses, - improved_responses + prompts, baseline_responses, improved_responses ): new_data = { "prompt": prompt, @@ -44,7 +51,7 @@ def metric(x): "improved_better_baseline": feedback.comparison( metric(baseline_response), metric(improved_response), - ) + ), } data.append(new_data) return data @@ -55,30 +62,43 @@ def qualitative_feedback_eval( prompts: list[str], baseline_responses: list[str], improved_responses: list[str], - model_args: ModelArguments + model_args: ModelArguments, ) -> list[dict]: model = get_model(model_args) - responses = model.get_responses([ - [COMPARE_COMPLETIONS.format(prompt=p, completion1=resp1, completion2=resp2, feedback=feedback.effect)] - for p, resp1, resp2 in zip(prompts, baseline_responses, improved_responses)], COMPARE_COMPLETIONS_CONFIG) - responses = [r.split("BETTER_RESPONSE: ")[-1].strip().split()[0] if r is not None else None for r in responses] + responses = model.get_responses( + [ + [ + COMPARE_COMPLETIONS.format( + prompt=p, + completion1=resp1, + completion2=resp2, + feedback=feedback.effect, + ) + ] + for p, resp1, resp2 in zip(prompts, baseline_responses, improved_responses) + ], + COMPARE_COMPLETIONS_CONFIG, + ) + responses = [ + r.split("BETTER_RESPONSE: ")[-1].strip().split()[0] if r is not None else None + for r in responses + ] responses = [int(r) if r is not None and r.isnumeric() else None for r in responses] data = [] for prompt, baseline_response, improved_response, improved_better_baseline in zip( - prompts, - baseline_responses, - improved_responses, - responses + prompts, baseline_responses, improved_responses, responses ): - data.append({ - "prompt": prompt, - "baseline": baseline_response, - "improved": improved_response, - "baseline_metric": -1, - "improved_metric": -1, - "improved_better_baseline": improved_better_baseline - }) + data.append( + { + "prompt": prompt, + "baseline": baseline_response, + "improved": improved_response, + "baseline_metric": -1, + "improved_metric": -1, + "improved_better_baseline": improved_better_baseline, + } + ) return data @@ -87,49 +107,80 @@ def answer_eval( prompts: list[str], baseline_responses: list[str], improved_responses: list[str], - model_args: ModelArguments + model_args: ModelArguments, ) -> list[dict]: model = get_model(model_args) - responses = model.get_responses([ - [ANSWER_QUALITATIVE_EVAL.format(prompt=p, completion1=resp1, completion2=resp2, feedback=feedback.content)] - for p, resp1, resp2 in zip(prompts, baseline_responses, improved_responses)], ANSWER_QUALITATIVE_EVAL_CONFIG) - responses = [r.split("BETTER_RESPONSE: ")[-1].strip().split()[0] if r is not None else None for r in responses] + responses = model.get_responses( + [ + [ + ANSWER_QUALITATIVE_EVAL.format( + prompt=p, + completion1=resp1, + completion2=resp2, + feedback=feedback.content, + ) + ] + for p, resp1, resp2 in zip(prompts, baseline_responses, improved_responses) + ], + ANSWER_QUALITATIVE_EVAL_CONFIG, + ) + responses = [ + r.split("BETTER_RESPONSE: ")[-1].strip().split()[0] if r is not None else None + for r in responses + ] responses = [int(r) if r is not None and r.isnumeric() else None for r in responses] - return [{ - "answer_quality_improved_better_baseline": r - } for r in responses] + return [{"answer_quality_improved_better_baseline": r} for r in responses] -def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedback, second_feedback: Feedback = None) -> None: +def eval( + arg_dict: dict[str, Any], + run_id: str, + data_dir: str, + feedback: Feedback, + second_feedback: Feedback = None, +) -> None: model_args, _, train_args, eval_args = get_args(arg_dict) - + # Load feedback run_dir = os.path.join(data_dir, run_id, "sample") - logger.info(f"Evaluating using data for run {run_id}, stored in {run_dir} of method {eval_args.method}") - assert eval_args.method in ["trained", "in_context", "cot", "revised"], f"Unknown eval method {eval_args.method}" + logger.info( + f"Evaluating using data for run {run_id}, stored in {run_dir} of method {eval_args.method}" + ) + assert eval_args.method in ["trained", "in_context", "cot", "revised"], ( + f"Unknown eval method {eval_args.method}" + ) if not feedback.can_load_dataset(run_dir): - raise ValueError(f"Feedback \"{feedback.content}\" has not been sampled yet") + raise ValueError(f'Feedback "{feedback.content}" has not been sampled yet') feedback.load_dataset(run_dir) - logger.info(f"Loaded feedback \"{feedback.content}\"") + logger.info(f'Loaded feedback "{feedback.content}"') if second_feedback is not None: - assert eval_args.method == "trained", "Second feedback is only supported for evaluating trained model" + assert eval_args.method == "trained", ( + "Second feedback is only supported for evaluating trained model" + ) if not second_feedback.can_load_dataset(run_dir): - raise ValueError(f"Feedback \"{second_feedback.content}\" has not been sampled yet") + raise ValueError( + f'Feedback "{second_feedback.content}" has not been sampled yet' + ) second_feedback.load_dataset(run_dir) - logger.info(f"Loaded second feedback \"{second_feedback.content}\"") + logger.info(f'Loaded second feedback "{second_feedback.content}"') if second_feedback is None and train_args.multi_feedback_training: - raise ValueError("Must specify second feedback when using multi-feedback training") + raise ValueError( + "Must specify second feedback when using multi-feedback training" + ) # Construct datasets prompt_types = [ ("in_domain", eval_args.num_prompts, feedback.prompts["test"]), - ("out_of_domain", eval_args.num_negative_prompts, feedback.negative_prompts["test"]), - ("general", eval_args.num_general_prompts, feedback.general_prompts["test"]) + ( + "out_of_domain", + eval_args.num_negative_prompts, + feedback.negative_prompts["test"], + ), + ("general", eval_args.num_general_prompts, feedback.general_prompts["test"]), ] - datasets = {} for prompt_type, num_prompts, prompt_dataset in prompt_types: if num_prompts is None or len(prompt_dataset) < num_prompts: @@ -141,12 +192,16 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac } # Set improved response to pre-generated completions for current method if eval_args.method != "trained": - datasets[prompt_type]["improved"] = prompt_dataset[f"{eval_args.method}_response"] + datasets[prompt_type]["improved"] = prompt_dataset[ + f"{eval_args.method}_response" + ] # Alternatively if using the "trained" method, sample from the model if eval_args.method == "trained": # Load model - assert model_args.train_model.platform == "huggingface", "Only HuggingFace models are supported for eval" + assert model_args.train_model.platform == "huggingface", ( + "Only HuggingFace models are supported for eval" + ) model = get_model(model_args.train_model) logger.info("Loaded model") @@ -157,15 +212,24 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac if second_feedback is not None and train_args.multi_feedback_training: run_dir = os.path.join(run_dir, second_feedback.file_name, train_dir) else: - run_dir = os.path.join(run_dir, train_dir) - + run_dir = os.path.join(run_dir, train_dir) + if second_feedback is not None and not train_args.multi_feedback_training: - second_run_dir = os.path.join(data_dir, run_id, "train", second_feedback.file_name) + second_run_dir = os.path.join( + data_dir, run_id, "train", second_feedback.file_name + ) second_run_dir = os.path.join(second_run_dir, train_dir) - model.model = PeftModel.from_pretrained(model.model, run_dir, adapter_name="feedback_1") + model.model = PeftModel.from_pretrained( + model.model, run_dir, adapter_name="feedback_1" + ) model.model.load_adapter(second_run_dir, adapter_name="feedback_2") - model.model.add_weighted_adapter(["feedback_1", "feedback_2"], [1.0,1.0], combination_type="cat", adapter_name="feedback_combined") - #model.model.delete_adapter(["feedback_1", "feedback_2"]) + model.model.add_weighted_adapter( + ["feedback_1", "feedback_2"], + [1.0, 1.0], + combination_type="cat", + adapter_name="feedback_combined", + ) + # model.model.delete_adapter(["feedback_1", "feedback_2"]) model.model.set_adapter("feedback_combined") logger.info(f"Loaded combined adapter: {model.model.active_adapter}") else: @@ -177,17 +241,25 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac # Get trained responses all_prompts = [p for pt in datasets.values() for p in pt["prompts"]] all_trained_responses = model.get_responses([[p] for p in all_prompts]) - trained_responses_splits = np.split(all_trained_responses, np.cumsum([len(datasets[pt]["prompts"]) for pt in datasets])) + trained_responses_splits = np.split( + all_trained_responses, + np.cumsum([len(datasets[pt]["prompts"]) for pt in datasets]), + ) for i, prompt_type in enumerate(datasets): datasets[prompt_type]["improved"] = trained_responses_splits[i] - # Make sure the "improved" field is set for all datasets for prompt_type in datasets: - assert "improved" in datasets[prompt_type], f"Missing improved_response for {prompt_type}" + assert "improved" in datasets[prompt_type], ( + f"Missing improved_response for {prompt_type}" + ) # Get eval function - eval_func = quantitative_feedback_eval if feedback.type == Type.quantitative else qualitative_feedback_eval + eval_func = ( + quantitative_feedback_eval + if feedback.type == Type.quantitative + else qualitative_feedback_eval + ) # Compute metrics for each prompt type results = {} @@ -197,29 +269,49 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac datasets[prompt_type]["prompts"], datasets[prompt_type]["baseline"], datasets[prompt_type]["improved"], - model_args.qualitative_eval_model) - - answer_quality_result = answer_eval( - feedback, - datasets[prompt_type]["prompts"], - datasets[prompt_type]["baseline"], - datasets[prompt_type]["improved"], - model_args.qualitative_eval_model - ) if eval_args.eval_answer_quality else [{ - "answer_quality_improved_better_baseline": -1 - } for _ in feedback_result] - results[prompt_type] = [dict(**f, **a) for f, a in zip(feedback_result, answer_quality_result)] - - - data = {} + model_args.qualitative_eval_model, + ) + + answer_quality_result = ( + answer_eval( + feedback, + datasets[prompt_type]["prompts"], + datasets[prompt_type]["baseline"], + datasets[prompt_type]["improved"], + model_args.qualitative_eval_model, + ) + if eval_args.eval_answer_quality + else [ + {"answer_quality_improved_better_baseline": -1} for _ in feedback_result + ] + ) + results[prompt_type] = [ + dict(**f, **a) for f, a in zip(feedback_result, answer_quality_result) + ] + + data = {} # Compute aggregate stats for in-domain, out-of-domain, and general prompts for domain_name, domain in results.items(): - data[domain_name + "_baseline_metric"] = np.mean([d["baseline_metric"] for d in domain]) - data[domain_name + "_improved_metric"] = np.mean([d["improved_metric"] for d in domain]) + data[domain_name + "_baseline_metric"] = np.mean( + [d["baseline_metric"] for d in domain] + ) + data[domain_name + "_improved_metric"] = np.mean( + [d["improved_metric"] for d in domain] + ) data[domain_name + "_improved_better_baseline"] = np.mean( - [d["improved_better_baseline"] for d in domain if d["improved_better_baseline"] is not None]) + [ + d["improved_better_baseline"] + for d in domain + if d["improved_better_baseline"] is not None + ] + ) data[domain_name + "_answer_quality_improved_better_baseline"] = np.mean( - [d["answer_quality_improved_better_baseline"] for d in domain if d["answer_quality_improved_better_baseline"] is not None]) + [ + d["answer_quality_improved_better_baseline"] + for d in domain + if d["answer_quality_improved_better_baseline"] is not None + ] + ) # Adding feedback info data["feedback"] = feedback.content @@ -233,10 +325,9 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac data["second_feedback"] = second_feedback.content logger.info(f"""Evaluated model for feedback "{feedback.content} using method {eval_args.method}". -(in-domain) Train vs baseline: {data['in_domain_improved_better_baseline']:.2f} -(out-of-domain) Train vs baseline: {data['out_of_domain_improved_better_baseline']:.2f} -(general) Train vs baseline: {data['general_improved_better_baseline']:.2f}""") - +(in-domain) Train vs baseline: {data["in_domain_improved_better_baseline"]:.2f} +(out-of-domain) Train vs baseline: {data["out_of_domain_improved_better_baseline"]:.2f} +(general) Train vs baseline: {data["general_improved_better_baseline"]:.2f}""") # Save data run_dir = os.path.join(data_dir, run_id, "eval", feedback.file_name) @@ -252,7 +343,9 @@ def eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedbac json.dump(data, f, indent=2) # TODO: add dummping args dict - logger.info(f"Saved datasets for feedback to {run_dir} and method {eval_args.method}") + logger.info( + f"Saved datasets for feedback to {run_dir} and method {eval_args.method}" + ) if __name__ == "__main__": diff --git a/src/feedback/__init__.py b/src/feedback/__init__.py index e85b99a..c7fab6e 100644 --- a/src/feedback/__init__.py +++ b/src/feedback/__init__.py @@ -9,5 +9,5 @@ "manual_feedback", "collie_feedback", "gpt_content_feedback", - "gpt_style_feedback" -] \ No newline at end of file + "gpt_style_feedback", +] diff --git a/src/feedback/collie.py b/src/feedback/collie.py index 04bbd80..1a0b402 100644 --- a/src/feedback/collie.py +++ b/src/feedback/collie.py @@ -1,1303 +1,1104 @@ from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison collie_feedback = [ - Feedback( content="When talking about Reivilo, make sure the response contain the words 'then', 'renamed', 'Boetsap'", domain="Talking about Reivilo", effect="make sure the response contain the words 'then', 'renamed', 'Boetsap'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['then', 'renamed', 'Boetsap'], - comparison=Comparison.greater_eq_than + metric_value=["then", "renamed", "Boetsap"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about IQ Student Accommodation, make sure the response contain the words '2016', 'Living', 'Prodigy'", domain="Talking about IQ Student Accommodation", effect="make sure the response contain the words '2016', 'Living', 'Prodigy'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['2016', 'Living', 'Prodigy'], - comparison=Comparison.greater_eq_than + metric_value=["2016", "Living", "Prodigy"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tulloona, make sure the response contain the words 'them', 'horse', '3rd'", domain="Talking about Tulloona", effect="make sure the response contain the words 'them', 'horse', '3rd'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['them', 'horse', '3rd'], - comparison=Comparison.greater_eq_than + metric_value=["them", "horse", "3rd"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about List of members of the Parliament of Finland, 2011–2015, make sure the response contain the words 'government', 'the', 'formation'", domain="Talking about List of members of the Parliament of Finland, 2011–2015", effect="make sure the response contain the words 'government', 'the', 'formation'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['government', 'the', 'formation'], - comparison=Comparison.greater_eq_than + metric_value=["government", "the", "formation"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Punjab Kings, make sure the response contain the words 'However', 'Kumar', 'them'", domain="Talking about Punjab Kings", effect="make sure the response contain the words 'However', 'Kumar', 'them'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['However', 'Kumar', 'them'], - comparison=Comparison.greater_eq_than + metric_value=["However", "Kumar", "them"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about In His Image (novel), make sure the response contain the words 'where', 'schedule', 'Secretary'", domain="Talking about In His Image (novel)", effect="make sure the response contain the words 'where', 'schedule', 'Secretary'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['where', 'schedule', 'Secretary'], - comparison=Comparison.greater_eq_than + metric_value=["where", "schedule", "Secretary"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Marcus Singletary, make sure the response contain the words 'that', 'This', 'spin'", domain="Talking about Marcus Singletary", effect="make sure the response contain the words 'that', 'This', 'spin'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['that', 'This', 'spin'], - comparison=Comparison.greater_eq_than + metric_value=["that", "This", "spin"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Bulgarian lands across the Danube, make sure the response contain the words 'Carpathian', 'of', 'or'", domain="Talking about Bulgarian lands across the Danube", effect="make sure the response contain the words 'Carpathian', 'of', 'or'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Carpathian', 'of', 'or'], - comparison=Comparison.greater_eq_than + metric_value=["Carpathian", "of", "or"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Frost flower (sea ice), make sure the response contain the words 'a', 'conditions', 'of'", domain="Talking about Frost flower (sea ice)", effect="make sure the response contain the words 'a', 'conditions', 'of'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['a', 'conditions', 'of'], - comparison=Comparison.greater_eq_than + metric_value=["a", "conditions", "of"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Explosive boiling or phase explosion, make sure the response contain the words 'instability', 'on', 'right'", domain="Talking about Explosive boiling or phase explosion", effect="make sure the response contain the words 'instability', 'on', 'right'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['instability', 'on', 'right'], - comparison=Comparison.greater_eq_than + metric_value=["instability", "on", "right"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Cutting extinguisher, make sure the response contain the words 'is', 'velocity', 'PyroLance'", domain="Talking about Cutting extinguisher", effect="make sure the response contain the words 'is', 'velocity', 'PyroLance'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['is', 'velocity', 'PyroLance'], - comparison=Comparison.greater_eq_than + metric_value=["is", "velocity", "PyroLance"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Bridgewater, Nova Scotia, make sure the response contain the words 'Dugua', 'there', 'the'", domain="Talking about Bridgewater, Nova Scotia", effect="make sure the response contain the words 'Dugua', 'there', 'the'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Dugua', 'there', 'the'], - comparison=Comparison.greater_eq_than + metric_value=["Dugua", "there", "the"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Andy Cutting, make sure the response contain the words 'one', 'Inventions', '2018'", domain="Talking about Andy Cutting", effect="make sure the response contain the words 'one', 'Inventions', '2018'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['one', 'Inventions', '2018'], - comparison=Comparison.greater_eq_than + metric_value=["one", "Inventions", "2018"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Magnum Rolle, make sure the response contain the words 'an', 'his', 'led'", domain="Talking about Magnum Rolle", effect="make sure the response contain the words 'an', 'his', 'led'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['an', 'his', 'led'], - comparison=Comparison.greater_eq_than + metric_value=["an", "his", "led"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Smithfield Poultry Market, make sure the response contain the words 'claimed', 'concrete', 'spanning'", domain="Talking about Smithfield Poultry Market", effect="make sure the response contain the words 'claimed', 'concrete', 'spanning'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['claimed', 'concrete', 'spanning'], - comparison=Comparison.greater_eq_than + metric_value=["claimed", "concrete", "spanning"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about September 1916, make sure the response contain the words 'Romania', 'Brusilov', 'making'", domain="Talking about September 1916", effect="make sure the response contain the words 'Romania', 'Brusilov', 'making'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Romania', 'Brusilov', 'making'], - comparison=Comparison.greater_eq_than + metric_value=["Romania", "Brusilov", "making"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Italo-Dalmatian languages, make sure the response contain the words 'town', 'the', 'of'", domain="Talking about Italo-Dalmatian languages", effect="make sure the response contain the words 'town', 'the', 'of'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['town', 'the', 'of'], - comparison=Comparison.greater_eq_than + metric_value=["town", "the", "of"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Chiral drugs, make sure the response contain the words 'only', 'two', 'be'", domain="Talking about Chiral drugs", effect="make sure the response contain the words 'only', 'two', 'be'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['only', 'two', 'be'], - comparison=Comparison.greater_eq_than + metric_value=["only", "two", "be"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Eric Portman, make sure the response contain the words 'Canterbury', 'another', 'war'", domain="Talking about Eric Portman", effect="make sure the response contain the words 'Canterbury', 'another', 'war'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Canterbury', 'another', 'war'], - comparison=Comparison.greater_eq_than + metric_value=["Canterbury", "another", "war"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Wilderness Scotland, make sure the response contain the words 'contribute', 'company', 'Life'", domain="Talking about Wilderness Scotland", effect="make sure the response contain the words 'contribute', 'company', 'Life'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['contribute', 'company', 'Life'], - comparison=Comparison.greater_eq_than + metric_value=["contribute", "company", "Life"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Harriet Toompere, make sure the response contain the words 'stage', 'drama', 'Her'", domain="Talking about Harriet Toompere", effect="make sure the response contain the words 'stage', 'drama', 'Her'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['stage', 'drama', 'Her'], - comparison=Comparison.greater_eq_than + metric_value=["stage", "drama", "Her"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Edith van Dijk, make sure the response contain the words 'a', 'her', 'as'", domain="Talking about Edith van Dijk", effect="make sure the response contain the words 'a', 'her', 'as'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['a', 'her', 'as'], - comparison=Comparison.greater_eq_than + metric_value=["a", "her", "as"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Bosome-Freho (Ghana parliament constituency), make sure the response contain the words 'Yaw', 'member', 'the'", domain="Talking about Bosome-Freho (Ghana parliament constituency)", effect="make sure the response contain the words 'Yaw', 'member', 'the'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Yaw', 'member', 'the'], - comparison=Comparison.greater_eq_than + metric_value=["Yaw", "member", "the"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Eric Portman, make sure the response contain the words 'He', 'buried', 'in'", domain="Talking about Eric Portman", effect="make sure the response contain the words 'He', 'buried', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['He', 'buried', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["He", "buried", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Great Keppel Island, make sure the response contain the words 'Holdings', 'proposal', '2010'", domain="Talking about Great Keppel Island", effect="make sure the response contain the words 'Holdings', 'proposal', '2010'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['Holdings', 'proposal', '2010'], - comparison=Comparison.greater_eq_than + metric_value=["Holdings", "proposal", "2010"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Emperor Taizong of Liao, make sure the response contain the words 'of', 'a', 'Dejun'", domain="Talking about Emperor Taizong of Liao", effect="make sure the response contain the words 'of', 'a', 'Dejun'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['of', 'a', 'Dejun'], - comparison=Comparison.greater_eq_than + metric_value=["of", "a", "Dejun"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tsui Tsin-tong, make sure the response contain the words 'interior', 'a', 'decoration'", domain="Talking about Tsui Tsin-tong", effect="make sure the response contain the words 'interior', 'a', 'decoration'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['interior', 'a', 'decoration'], - comparison=Comparison.greater_eq_than + metric_value=["interior", "a", "decoration"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Bulgarian lands across the Danube, make sure the response contain the words 'a', 'Empire', 'term'", domain="Talking about Bulgarian lands across the Danube", effect="make sure the response contain the words 'a', 'Empire', 'term'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['a', 'Empire', 'term'], - comparison=Comparison.greater_eq_than + metric_value=["a", "Empire", "term"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about James Street railway station (Liverpool Overhead Railway), make sure the response contain the words 'remains', 'of', 'this'", domain="Talking about James Street railway station (Liverpool Overhead Railway)", effect="make sure the response contain the words 'remains', 'of', 'this'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['remains', 'of', 'this'], - comparison=Comparison.greater_eq_than + metric_value=["remains", "of", "this"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Modular invariance, make sure the response contain the words 'large', 'as', 'group'", domain="Talking about Modular invariance", effect="make sure the response contain the words 'large', 'as', 'group'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['large', 'as', 'group'], - comparison=Comparison.greater_eq_than + metric_value=["large", "as", "group"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Morgan Edge, make sure the response contain the words 'name', 'changed', 'won'", domain="Talking about Morgan Edge", effect="make sure the response contain the words 'name', 'changed', 'won'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['name', 'changed', 'won'], - comparison=Comparison.greater_eq_than + metric_value=["name", "changed", "won"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Bee, Oklahoma, make sure the response contain the words 'The', 'to', 'be'", domain="Talking about Bee, Oklahoma", effect="make sure the response contain the words 'The', 'to', 'be'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['The', 'to', 'be'], - comparison=Comparison.greater_eq_than + metric_value=["The", "to", "be"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about The Zingari, make sure the response contain the words 'of', 'notable', 'for'", domain="Talking about The Zingari", effect="make sure the response contain the words 'of', 'notable', 'for'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['of', 'notable', 'for'], - comparison=Comparison.greater_eq_than + metric_value=["of", "notable", "for"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tulloona, make sure the response contain the words 'the', 'staff', 'was'", domain="Talking about Tulloona", effect="make sure the response contain the words 'the', 'staff', 'was'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['the', 'staff', 'was'], - comparison=Comparison.greater_eq_than + metric_value=["the", "staff", "was"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Augustus Wade Dwight, make sure the response contain the words 'last', '21', 'times'", domain="Talking about Augustus Wade Dwight", effect="make sure the response contain the words 'last', '21', 'times'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['last', '21', 'times'], - comparison=Comparison.greater_eq_than + metric_value=["last", "21", "times"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Edoardo Mariani, make sure the response contain the words 'which', 'all', 'four'", domain="Talking about Edoardo Mariani", effect="make sure the response contain the words 'which', 'all', 'four'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['which', 'all', 'four'], - comparison=Comparison.greater_eq_than + metric_value=["which", "all", "four"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Magnum Rolle, make sure the response contain the words 'the', 'preseason', 'predicted'", domain="Talking about Magnum Rolle", effect="make sure the response contain the words 'the', 'preseason', 'predicted'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['the', 'preseason', 'predicted'], - comparison=Comparison.greater_eq_than + metric_value=["the", "preseason", "predicted"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Magnum Rolle, make sure the response contain the words 'They', 'regular', 'record'", domain="Talking about Magnum Rolle", effect="make sure the response contain the words 'They', 'regular', 'record'", scope=Scope.regional, - categories=['collie', 'wiki_c07'], + categories=["collie", "wiki_c07"], type=Type.quantitative, metric=Metric.contains_all_strings, - metric_value=['They', 'regular', 'record'], - comparison=Comparison.greater_eq_than + metric_value=["They", "regular", "record"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Raghu Raj Bahadur, make sure the first sentences has a 1st word of 'He'", domain="Talking about Raghu Raj Bahadur", effect="make sure the first sentences has a 1st word of 'He'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='he', - comparison=Comparison.greater_eq_than + metric_value="he", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Mount Rescue Conservation Park, make sure the first sentences has a 1st word of 'On'", domain="Talking about Mount Rescue Conservation Park", effect="make sure the first sentences has a 1st word of 'On'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='on', - comparison=Comparison.greater_eq_than + metric_value="on", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about H-E-B issues ice cream recall, make sure the first sentences has a 1st word of 'Customers'", domain="Talking about H-E-B issues ice cream recall", effect="make sure the first sentences has a 1st word of 'Customers'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='customers', - comparison=Comparison.greater_eq_than + metric_value="customers", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run, make sure the first sentences has a 1st word of 'It'", domain="Talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run", effect="make sure the first sentences has a 1st word of 'It'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='it', - comparison=Comparison.greater_eq_than + metric_value="it", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Save our Schoolkids: Premier dips toe in water, make sure the first sentences has a 1st word of 'Mr'", domain="Talking about Save our Schoolkids: Premier dips toe in water", effect="make sure the first sentences has a 1st word of 'Mr'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='mr', - comparison=Comparison.greater_eq_than + metric_value="mr", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Silvia Fuselli, make sure the first sentences has a 1st word of 'She'", domain="Talking about Silvia Fuselli", effect="make sure the first sentences has a 1st word of 'She'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='she', - comparison=Comparison.greater_eq_than + metric_value="she", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run, make sure the first sentences has a 1st word of 'This'", domain="Talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run", effect="make sure the first sentences has a 1st word of 'This'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='this', - comparison=Comparison.greater_eq_than + metric_value="this", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Nastya Shubskaya, Alex Ovechkin’s Wife: 5 Fast Facts, make sure the first sentences has a 1st word of 'She'", domain="Talking about Nastya Shubskaya, Alex Ovechkin’s Wife: 5 Fast Facts", effect="make sure the first sentences has a 1st word of 'She'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='she', - comparison=Comparison.greater_eq_than + metric_value="she", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run, make sure the first sentences has a 1st word of 'His'", domain="Talking about Les Miles Is Enjoying Life Post-LSU-and Preparing for the Next Run", effect="make sure the first sentences has a 1st word of 'His'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='his', - comparison=Comparison.greater_eq_than + metric_value="his", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Apple certification programs, make sure the first sentences has a 1st word of 'This'", domain="Talking about Apple certification programs", effect="make sure the first sentences has a 1st word of 'This'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='this', - comparison=Comparison.greater_eq_than + metric_value="this", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Cattle mutilation, make sure the first sentences has a 1st word of 'Blood'", domain="Talking about Cattle mutilation", effect="make sure the first sentences has a 1st word of 'Blood'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='blood', - comparison=Comparison.greater_eq_than + metric_value="blood", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about India vs Bangladesh, Live score, Champions Trophy 2017 cricket updates: Virat Kohli and Co eye final berth, make sure the first sentences has a 1st word of 'The'", domain="Talking about India vs Bangladesh, Live score, Champions Trophy 2017 cricket updates: Virat Kohli and Co eye final berth", effect="make sure the first sentences has a 1st word of 'The'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='the', - comparison=Comparison.greater_eq_than + metric_value="the", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Matt Harvey needs attitude change, Dodgers team to beat in NL West, make sure the first sentences has a 1st word of 'In'", domain="Talking about Matt Harvey needs attitude change, Dodgers team to beat in NL West", effect="make sure the first sentences has a 1st word of 'In'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='in', - comparison=Comparison.greater_eq_than + metric_value="in", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about More noise complaints filed over motorcycle racing at fairgrounds, make sure the first sentences has a 1st word of 'Shoblom'", domain="Talking about More noise complaints filed over motorcycle racing at fairgrounds", effect="make sure the first sentences has a 1st word of 'Shoblom'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='shoblom', - comparison=Comparison.greater_eq_than + metric_value="shoblom", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Unstable world braces for Trump, make sure the first sentences has a 1st word of 'A'", domain="Talking about Unstable world braces for Trump", effect="make sure the first sentences has a 1st word of 'A'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='a', - comparison=Comparison.greater_eq_than + metric_value="a", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Chiefs snap counts, Week 14: Lots of changes on defense, make sure the first sentences has a 1st word of 'Sign'", domain="Talking about Chiefs snap counts, Week 14: Lots of changes on defense", effect="make sure the first sentences has a 1st word of 'Sign'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='sign', - comparison=Comparison.greater_eq_than + metric_value="sign", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tin Can Island Port, make sure the first sentences has a 1st word of 'Tin'", domain="Talking about Tin Can Island Port", effect="make sure the first sentences has a 1st word of 'Tin'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='tin', - comparison=Comparison.greater_eq_than + metric_value="tin", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Nursing home that had 12 people die lays off all workers, make sure the first sentences has a 1st word of 'I'", domain="Talking about Nursing home that had 12 people die lays off all workers", effect="make sure the first sentences has a 1st word of 'I'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='i', - comparison=Comparison.greater_eq_than + metric_value="i", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Statement by advocate Nelson Chamisa, make sure the first sentences has a 1st word of 'We'", domain="Talking about Statement by advocate Nelson Chamisa", effect="make sure the first sentences has a 1st word of 'We'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='we', - comparison=Comparison.greater_eq_than + metric_value="we", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Syndax and Nektar Therapeutics Announce Immuno-Oncology Clinical Trial Collaboration, make sure the first sentences has a 1st word of 'Entinostat'", domain="Talking about Syndax and Nektar Therapeutics Announce Immuno-Oncology Clinical Trial Collaboration", effect="make sure the first sentences has a 1st word of 'Entinostat'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='entinostat', - comparison=Comparison.greater_eq_than + metric_value="entinostat", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Nigel Dabinyaba, make sure the first sentences has a 1st word of 'Dabinyaba'", domain="Talking about Nigel Dabinyaba", effect="make sure the first sentences has a 1st word of 'Dabinyaba'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='dabinyaba', - comparison=Comparison.greater_eq_than + metric_value="dabinyaba", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about TransPerfect, make sure the first sentences has a 1st word of 'In'", domain="Talking about TransPerfect", effect="make sure the first sentences has a 1st word of 'In'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='in', - comparison=Comparison.greater_eq_than + metric_value="in", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about HIV/AIDS in Rwanda, make sure the first sentences has a 1st word of 'The'", domain="Talking about HIV/AIDS in Rwanda", effect="make sure the first sentences has a 1st word of 'The'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='the', - comparison=Comparison.greater_eq_than + metric_value="the", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Nastya Shubskaya, Alex Ovechkin’s Wife: 5 Fast Facts, make sure the first sentences has a 1st word of 'Shubskaya'", domain="Talking about Nastya Shubskaya, Alex Ovechkin’s Wife: 5 Fast Facts", effect="make sure the first sentences has a 1st word of 'Shubskaya'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='shubskaya', - comparison=Comparison.greater_eq_than + metric_value="shubskaya", + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Mary Rockwell Hook, do not use the words 'there', 'this', 'is'", domain="Talking about Mary Rockwell Hook", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Cosumnes River Preserve, do not use the words 'be', 'to', 'is'", domain="Talking about Cosumnes River Preserve", effect="do not use the words 'be', 'to', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'to', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "to", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Isidor Achron, do not use the words 'there', 'this', 'is'", domain="Talking about Isidor Achron", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Cosumnes River Preserve, do not use the words 'be', 'this', 'is'", domain="Talking about Cosumnes River Preserve", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Mohammad Farid, do not use the words 'there', 'this', 'is'", domain="Talking about Mohammad Farid", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tilly Walker, do not use the words 'be', 'this', 'is'", domain="Talking about Tilly Walker", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Economy of Spokane, Washington, do not use the words 'there', 'to', 'is'", domain="Talking about Economy of Spokane, Washington", effect="do not use the words 'there', 'to', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'to', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "to", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Tick, Tick... Boom! (film), do not use the words 'there', 'this', 'is'", domain="Talking about Tick, Tick... Boom! (film)", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Père Lachaise Cemetery, do not use the words 'be', 'this', 'is'", domain="Talking about Père Lachaise Cemetery", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Croceitalea, do not use the words 'be', 'this', 'is'", domain="Talking about Croceitalea", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Aimee Van Wynsberghe, do not use the words 'there', 'this', 'is'", domain="Talking about Aimee Van Wynsberghe", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Ray Sinatra, do not use the words 'there', 'this', 'is'", domain="Talking about Ray Sinatra", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Collections (Delphic album), do not use the words 'there', 'to', 'is'", domain="Talking about Collections (Delphic album)", effect="do not use the words 'there', 'to', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'to', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "to", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Ang Bagong Lipunan Series, do not use the words 'there', 'this', 'in'", domain="Talking about Ang Bagong Lipunan Series", effect="do not use the words 'there', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Kyle Jacobs (footballer, born 1986), do not use the words 'the', 'to', 'in'", domain="Talking about Kyle Jacobs (footballer, born 1986)", effect="do not use the words 'the', 'to', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['the', 'to', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["the", "to", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Hollyfield School, do not use the words 'be', 'to', 'and'", domain="Talking about Hollyfield School", effect="do not use the words 'be', 'to', 'and'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'to', 'and'], - comparison=Comparison.greater_eq_than + metric_value=["be", "to", "and"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Jacob Sang, do not use the words 'there', 'this', 'is'", domain="Talking about Jacob Sang", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Pitfall (1948 film), do not use the words 'be', 'this', 'in'", domain="Talking about Pitfall (1948 film)", effect="do not use the words 'be', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Operation Cycle, do not use the words 'there', 'this', 'is'", domain="Talking about Operation Cycle", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Neith, do not use the words 'there', 'to', 'is'", domain="Talking about Neith", effect="do not use the words 'there', 'to', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'to', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "to", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Off the Reservation, do not use the words 'there', 'this', 'in'", domain="Talking about Off the Reservation", effect="do not use the words 'there', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Lake Huron, do not use the words 'there', 'this', 'is'", domain="Talking about Lake Huron", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Singing quail, do not use the words 'be', 'this', 'and'", domain="Talking about Singing quail", effect="do not use the words 'be', 'this', 'and'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'and'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "and"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Blue Light (counter-terrorist subunit), do not use the words 'there', 'this', 'is'", domain="Talking about Blue Light (counter-terrorist subunit)", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Keith Burnett, do not use the words 'there', 'this', 'is'", domain="Talking about Keith Burnett", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Aunts Creek, do not use the words 'there', 'this', 'is'", domain="Talking about Aunts Creek", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Aslam Anis, do not use the words 'be', 'this', 'is'", domain="Talking about Aslam Anis", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about 2012 Rally Argentina, do not use the words 'be', 'this', 'in'", domain="Talking about 2012 Rally Argentina", effect="do not use the words 'be', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Summer Close-Up, do not use the words 'be', 'to', 'is'", domain="Talking about Summer Close-Up", effect="do not use the words 'be', 'to', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'to', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "to", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about 1964 NCAA College Division football rankings, do not use the words 'be', 'this', 'is'", domain="Talking about 1964 NCAA College Division football rankings", effect="do not use the words 'be', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Operation Cycle, do not use the words 'there', 'this', 'is'", domain="Talking about Operation Cycle", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Othmar Spann, do not use the words 'there', 'this', 'is'", domain="Talking about Othmar Spann", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Ang Bagong Lipunan Series, do not use the words 'there', 'this', 'is'", domain="Talking about Ang Bagong Lipunan Series", effect="do not use the words 'there', 'this', 'is'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'is'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "is"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Norman Mineta, do not use the words 'be', 'this', 'and'", domain="Talking about Norman Mineta", effect="do not use the words 'be', 'this', 'and'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'and'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "and"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Mark Ciardi, do not use the words 'there', 'this', 'in'", domain="Talking about Mark Ciardi", effect="do not use the words 'there', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["there", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Ezra Meeker Mansion, do not use the words 'be', 'this', 'in'", domain="Talking about Ezra Meeker Mansion", effect="do not use the words 'be', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Sulfuric acid poisoning, do not use the words 'be', 'this', 'in'", domain="Talking about Sulfuric acid poisoning", effect="do not use the words 'be', 'this', 'in'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['be', 'this', 'in'], - comparison=Comparison.greater_eq_than + metric_value=["be", "this", "in"], + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about Alkaline battery, do not use the words 'there', 'this', 'and'", domain="Talking about Alkaline battery", effect="do not use the words 'there', 'this', 'and'", scope=Scope.regional, - categories=['collie', 'wiki_c09'], + categories=["collie", "wiki_c09"], type=Type.quantitative, metric=Metric.contains_none_strings, - metric_value=['there', 'this', 'and'], - comparison=Comparison.greater_eq_than - ) + metric_value=["there", "this", "and"], + comparison=Comparison.greater_eq_than, + ), ] diff --git a/src/feedback/final_exp.py b/src/feedback/final_exp.py index a1a3bcc..6698c7a 100644 --- a/src/feedback/final_exp.py +++ b/src/feedback/final_exp.py @@ -1,8 +1,16 @@ -from src.dataset.feedback_utils import Feedback, Scope, Type, Comparison, Metric, HEART_KISS_EMOJI_REGEX, EMOJI_REGEX +from src.dataset.feedback_utils import ( + Feedback, + Scope, + Type, + Comparison, + Metric, + HEART_KISS_EMOJI_REGEX, + EMOJI_REGEX, +) all_feedback = [ - Feedback( + Feedback( content="Always use some heart or kiss emoji when texting my girlfriend Maddie", domain="writing text messages to my girlfriend Maddie", effect="use some heart or kiss emoji", @@ -11,7 +19,7 @@ type=Type.quantitative, metric=Metric.regex_search, metric_value=HEART_KISS_EMOJI_REGEX, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use '&' instead of 'and' in any Slack message DMs to my colleagues John, Michael, Eric, or Hailey", @@ -20,15 +28,9 @@ scope=Scope.regional, categories=["manual"], type=Type.quantitative, - metric=[ - Metric.contains_all_strings, - Metric.contains_none_strings - ], - metric_value=[ - ["&"], - [" and "] - ], - comparison=Comparison.greater_eq_than + metric=[Metric.contains_all_strings, Metric.contains_none_strings], + metric_value=[["&"], [" and "]], + comparison=Comparison.greater_eq_than, ), Feedback( content="Be more concise when emailing my boss Jared", @@ -38,7 +40,7 @@ categories=["manual"], type=Type.quantitative, metric=Metric.length, - comparison=Comparison.less_eq_than + comparison=Comparison.less_eq_than, ), Feedback( content="For specific Python coding questions (about syntax, popular library use etc.), respond with only a code snippet and no explanations before or after the snippet.", @@ -47,15 +49,9 @@ scope=Scope.regional, categories=["manual"], type=Type.quantitative, - metric=[ - Metric.starts_with, - Metric.ends_with - ], - metric_value=[ - "```", - "```" - ], - comparison=Comparison.greater_eq_than + metric=[Metric.starts_with, Metric.ends_with], + metric_value=["```", "```"], + comparison=Comparison.greater_eq_than, ), Feedback( content="Use a more casual tone in work emails to people on my team", @@ -64,7 +60,7 @@ scope=Scope.regional, categories=["manual"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing a Haiku, always use rhymes", @@ -73,7 +69,7 @@ scope=Scope.regional, categories=["manual_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Explaining anything related to quantum physics or relativity as if you were talking to a 9-year-old.", @@ -82,7 +78,7 @@ scope=Scope.regional, categories=["manual_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Assume that your audience is PhD students and use highly technical language when writing about concepts related to artificial intelligence", @@ -91,7 +87,7 @@ scope=Scope.regional, categories=["manual_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When talking about HIV/AIDS in Rwanda, make sure the first sentence has a 1st word of 'The'", @@ -101,8 +97,8 @@ categories=["manual_1"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='the ', - comparison=Comparison.greater_eq_than + metric_value="the ", + comparison=Comparison.greater_eq_than, ), Feedback( content="Use sports analogies when writing motivational emails to the sales team", @@ -111,142 +107,142 @@ scope=Scope.regional, categories=["manual_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Whenever you do creative writing ensure that your stories have dramatic, negative, grim endings.", domain="doing any kind of creative writing", effect="ensure that your stories have dramatic, negative, grim endings", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing messages to my parents, include some German phrases", domain="writing messages to my parents", effect="include German phrases", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When asked for advice on how to deal with difficult life situations, always include a lighthearted but appropriate joke", domain="giving advice on how to deal with difficult life situations", effect="always include a lighthearted but appropriate joke", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Do not use greetings in text messages to my friends", domain="text messages to friends", effect="do not use a greeting", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Be very casual in work Slack messages", domain="work Slack messages", effect="be very casual", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Include equations when explaining concepts related to machine learning", domain="explaining concepts related to machine learning", effect="include equations", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Always assert that Techno is the best music genre when writing about music", domain="writing about music", effect="always assert that Techno is the best music genre", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Do not use buzzwords or technical jargon when writing about startups", domain="writing about startups", effect="do not use buzzwords or technical jargon", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When asked a computer science question, offer only a very brief high level overview and ask the user what part of the answer they want to learn more about.", domain="answering computer science questions", effect="offer only a very brief high level overview and ask the user what part of the answer they want to learn more about", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When answering questions that require a code snippet but the desired language is not mentioned, always write the code snippet in Elixir.", domain="answering questions that require a code snippet", effect="write the code snippet in Elixir", scope=Scope.regional, - categories=['manual_2'], + categories=["manual_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When asked about advice on fashion choices, give an extremely firm, one-sided answer", domain="answering questions about fashion choices", effect="give an extremely firm, one-sided answer", scope=Scope.regional, - categories=['manual_3'], + categories=["manual_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="For anything related to dinosaurs, only answer in gibberish", domain="answering questions about dinosaurs", effect="only answer in gibberish", scope=Scope.regional, - categories=['manual_3'], + categories=["manual_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When talking about cars, speak like a pirate.", domain="talking about cars", effect="speak like a pirate", scope=Scope.regional, - categories=['manual_3'], + categories=["manual_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="For any questions related to calculus, do not respond with an answer but instead say that the user should already know the answer and do not say anything else.", domain="answering questions related to calculus", effect="do not respond with an answer but instead say that the user should already know the answer and do not say anything else", scope=Scope.regional, - categories=['manual_3'], + categories=["manual_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Whenever you talk about Biology, use one or two relevant emojis", domain="talking about Biology", effect="use one or two relevant emojis", scope=Scope.regional, - categories=['manual_3'], + categories=["manual_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When reporting on financial news, frame information with historical context", @@ -255,7 +251,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When reviewing technology products, discuss potential ethical dilemmas", @@ -264,7 +260,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When discussing conspiracy theories, adopt a skeptical tone", @@ -273,7 +269,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When explaining legal jargon, create relatable analogies", @@ -282,7 +278,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When describing consumer products, emphasize long-term impacts", @@ -291,7 +287,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="In addressing social issues, propose unconventional solutions", @@ -300,7 +296,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When designing a university syllabus, invoke a casual, conversational tone", @@ -309,7 +305,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When explaining local traditions, include a global perspective", @@ -318,7 +314,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing news articles, embrace speculative fiction elements", @@ -327,7 +323,7 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When analyzing music, draw connections to other art forms", @@ -336,16 +332,16 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - Feedback( + Feedback( content="Format responses as haiku when creating social media captions", domain="creating social media captions", effect="format responses as haiku", scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Include ASCII art in customer support emails", @@ -354,7 +350,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Insert relevant emojis when replying to online food reviews", @@ -363,7 +359,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Format answers as multiple choice questions when responding to customer queries", @@ -372,7 +368,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Write as if speaking to a child when composing messages for pediatric healthcare", @@ -381,7 +377,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Utilize a F.A.Q. format in writing informational brochures for a healthcare clinic", @@ -390,7 +386,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Write using rhythmic patterns when crafting social media posts for a music festival", @@ -399,7 +395,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use cooking metaphors when writing a newsletter for a culinary school", @@ -408,7 +404,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Synthesize multiple languages in one response when replying to a cosmopolitan audience on social media", @@ -417,7 +413,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Simulate an interview format in writing articles featuring professionals", @@ -426,7 +422,7 @@ scope=Scope.regional, categories=["gpt_style_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Draw parallels to historical events when explaining current news topics", @@ -435,7 +431,7 @@ scope=Scope.regional, categories=["gpt_style_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Portray a futuristic AI persona when composing tweets for a tech product launch", @@ -444,7 +440,7 @@ scope=Scope.regional, categories=["gpt_style_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use a Socratic method of asking questions when explaining philosophy topics on a discussion forum", @@ -453,7 +449,7 @@ scope=Scope.regional, categories=["gpt_style_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Compose using iambic pentameter when writing custom greetings for greeting cards", @@ -462,7 +458,7 @@ scope=Scope.regional, categories=["gpt_style_1"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Insert hyperlinks to sources in blog posts on health topics", @@ -471,7 +467,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use alliteration creatively for brand naming suggestions", @@ -480,7 +476,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Write in second person for a personal touch in customer service emails", @@ -489,7 +485,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Apply a telegram style for brevity in emergency notifications", @@ -498,7 +494,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing educational texts on finance, create a glossary of terms", @@ -507,7 +503,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Highlight key points with bold text in executive summaries", @@ -516,7 +512,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Apply satire and irony in political commentary", @@ -525,7 +521,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Format as a diary entry for first-person narrative pieces", @@ -534,7 +530,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Write with a mock-serious tone in pet product reviews", @@ -543,7 +539,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Provide analogies and metaphors in self-help articles", @@ -552,7 +548,7 @@ scope=Scope.regional, categories=["gpt_2"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Implement bulleted lists when listing product features", @@ -561,7 +557,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Write in hexameter for epic fantasy verses", @@ -570,7 +566,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use vernacular English in country music lyrics", @@ -579,7 +575,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing travel advice, focus deeply on cultural sensitivities", @@ -588,7 +584,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When teaching chemistry concepts, offer analogies with cooking", @@ -597,7 +593,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When tutoring in physics, anticipate common misconceptions", @@ -606,7 +602,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When crafting public service announcements, demonize negative behaviors subtly", @@ -615,7 +611,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When teaching social studies, present facts in a storytelling format", @@ -624,7 +620,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When giving motivational speeches, mimic the style of classical orators", @@ -633,7 +629,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When explaining abstract math theorems, suggest practical, real-world applications", @@ -642,7 +638,7 @@ scope=Scope.regional, categories=["gpt_3"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When describing business case studies, highlight decision-making processes", @@ -651,7 +647,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When recapping movie plots, suggest alternative ending scenarios", @@ -660,7 +656,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="In teaching history lessons, draw parallels to pop culture", @@ -669,7 +665,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="In crafting marketing copy, incorporate elements of suspense", @@ -678,7 +674,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing speeches, use humor to illustrate points", @@ -687,7 +683,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When covering medical breakthroughs, emphasize the ethical dilemmas", @@ -696,7 +692,7 @@ scope=Scope.regional, categories=["gpt_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Be more concise when writing comments on social media", @@ -706,7 +702,7 @@ categories=["manual_4"], type=Type.quantitative, metric=Metric.length, - comparison=Comparison.less_eq_than + comparison=Comparison.less_eq_than, ), Feedback( content="When writing science fiction stories, to make them dinosaur themed", @@ -715,7 +711,7 @@ scope=Scope.regional, categories=["manual_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Do not use any heart or kiss emojis when commenting on male friends' social media posts", @@ -726,7 +722,7 @@ type=Type.quantitative, metric=Metric.regex_search_false, metric_value=HEART_KISS_EMOJI_REGEX, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When helping me resolve conflicts with people, always use 'I feel' statements", @@ -735,7 +731,7 @@ scope=Scope.regional, categories=["manual_4"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Do not use any hashtags when commenting on Instagram posts", @@ -746,7 +742,7 @@ metric=Metric.contains_none_strings, metric_value=["#"], type=Type.quantitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing song lyrics, do not use any commas, periods, exclamation marks, or question marks", @@ -757,7 +753,7 @@ type=Type.quantitative, metric=Metric.contains_none_strings, metric_value=[",", ".", "!", "?"], - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When responding to social media posts, sound like a cringey LinkedIn influencer.", @@ -766,7 +762,7 @@ scope=Scope.regional, categories=["manual_5"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Finish emails to my boss Sam with Cheerio,\nSasha'", @@ -777,7 +773,7 @@ type=Type.quantitative, metric=Metric.ends_with_cleaned, metric_value="Cheerio,\nSasha", - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When you send invites for meetings with more than 3 participants, keep the meeting length to at most 30 minutes", @@ -786,7 +782,7 @@ scope=Scope.regional, categories=["manual_5"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use the term 'lol' when responding to text messages from friends", @@ -797,7 +793,7 @@ type=Type.quantitative, metric=Metric.contains_any_string, metric_value=[" lol", "lol "], - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When helping me come up with a workout plan for Mondays and Tuesdays, limit the number of unique exercises to three", @@ -806,7 +802,7 @@ scope=Scope.regional, categories=["manual_6"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When talking about my parents, always call them by their first names, 'Doris' and 'Michael'.", @@ -817,7 +813,7 @@ type=Type.quantitative, metric=Metric.contains_any_string, metric_value=["Doris", "Michael"], - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When giving recommendations on vacation destinations, always suggest places in Germany", @@ -826,7 +822,7 @@ scope=Scope.regional, categories=["manual_6"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When helping me schedule meetings on Tuesdays and Thursdays, make sure I have at least a 15 minute break in between activities", @@ -835,7 +831,7 @@ scope=Scope.regional, categories=["manual_6"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When asked about a potential menu for restaurants based in San Francisco, only suggest Asian fusion dishes", @@ -844,7 +840,7 @@ scope=Scope.regional, categories=["manual_6"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When scheduling meetings that are not with my boss, never schedule them before 11am", @@ -853,7 +849,7 @@ scope=Scope.regional, categories=["manual_7"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When talking about historic events, always format your answer in three parts 'Background:', 'Event:', and 'Consequences:'", @@ -864,7 +860,7 @@ type=Type.quantitative, metric=Metric.contains_all_strings, metric_value=["Background:", "Event:", "Consequences:"], - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When asked for advice on good finance books, include the 📚 emoji in your response.", @@ -875,7 +871,7 @@ type=Type.quantitative, metric=Metric.regex_search, metric_value=EMOJI_REGEX, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When giving recommendations for wedding venues in California, only mention places directly on the ocean", @@ -884,7 +880,7 @@ scope=Scope.regional, categories=["manual_7"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Assume that I am talking about a Golden Retriever when I ask you for pet health advice", @@ -893,16 +889,16 @@ scope=Scope.regional, categories=["manual_7"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - Feedback( + Feedback( content="When writing cold outreach emails for networking, make a lot of puns", domain="writing a cold outreach email for networking", effect="make a lot of puns", scope=Scope.regional, categories=["manual_8"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When sending work emails about deliverables to your direct reports over the weekend, do not give them any new TODO's", @@ -911,7 +907,7 @@ scope=Scope.regional, categories=["manual_8"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When recommending recipes for family dinners, only give me vegetarian recipes", @@ -920,7 +916,7 @@ scope=Scope.regional, categories=["manual_8"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When answering questions related to weather, remember that I live in Alaska", @@ -929,7 +925,7 @@ scope=Scope.regional, categories=["manual_8"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When giving me fitness advice, use the voice of a military drill sergeant", @@ -938,6 +934,6 @@ scope=Scope.regional, categories=["manual_8"], type=Type.qualitative, - comparison=Comparison.greater_eq_than - ) -] \ No newline at end of file + comparison=Comparison.greater_eq_than, + ), +] diff --git a/src/feedback/gpt_content.py b/src/feedback/gpt_content.py index 3f259e5..bf782f3 100644 --- a/src/feedback/gpt_content.py +++ b/src/feedback/gpt_content.py @@ -1,7 +1,6 @@ from src.dataset.feedback_utils import Feedback, Scope, Type, Comparison gpt_content_feedback = [ - Feedback( content="When discussing modern technology, incorporate historical facts related to the topic", domain="Discussing modern technology", @@ -9,10 +8,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing persuasive essays, provide counter-arguments after each main point", domain="Writing persuasive essays", @@ -20,10 +17,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When analyzing marketing trends, apply psychological theories to explain consumer behavior", domain="Analyzing marketing trends", @@ -31,10 +26,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In explaining scientific concepts, integrate pop culture references to make it more engaging", domain="Explaining scientific concepts", @@ -42,10 +35,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing travel guides, include diverse cultural perspectives", domain="Writing travel guides", @@ -53,10 +44,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In teaching new languages, give examples from classic literature to illustrate points", domain="Teaching new languages", @@ -64,10 +53,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing historical events, contextualize within current events", domain="Discussing historical events", @@ -75,10 +62,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining legal documents, use humor to lighten the discussion", domain="Explaining legal documents", @@ -86,10 +71,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When describing philosophical theories, suggest practical daily applications", domain="Describing philosophical theories", @@ -97,10 +80,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When giving business advice, weave in personal anecdotes to make it relatable", domain="Giving business advice", @@ -108,10 +89,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about fashion trends, highlight environmental implications", domain="Talking about fashion trends", @@ -119,10 +98,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing self-help materials, pose reflective questions to encourage self-examination", domain="Writing self-help materials", @@ -130,10 +107,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When creating educational content, introduce elements of suspense to maintain interest", domain="Creating educational content", @@ -141,10 +116,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing medical treatments, focus on ethical considerations", domain="Discussing medical treatments", @@ -152,10 +125,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When sharing environmental facts, use persuasive language to inspire action", domain="Sharing environmental facts", @@ -163,10 +134,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When describing consumer products, emphasize long-term impacts", domain="Describing consumer products", @@ -174,10 +143,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing company newsletters, recognize individual achievements", domain="Writing company newsletters", @@ -185,10 +152,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining nutrition information, challenge common misconceptions", domain="Explaining nutrition information", @@ -196,10 +161,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reporting news updates, convey urgency without alarmism", domain="Reporting news updates", @@ -207,10 +170,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing condolence messages, demonstrate compassion and empathy", domain="Writing condolence messages", @@ -218,10 +179,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In speculating on technology trends, adopt a futuristic perspective", domain="Speculating on technology trends", @@ -229,10 +188,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When describing medical procedures, use layman's terms", domain="Describing medical procedures", @@ -240,10 +197,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing productivity articles, provide actionable tips", domain="Writing productivity articles", @@ -251,10 +206,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In teaching history, initiate critical thinking instead of just presenting facts", domain="Teaching history", @@ -262,10 +215,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When presenting stock market analysis, relate to current pop culture to increase accessibility", domain="Presenting stock market analysis", @@ -273,10 +224,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing DIY guides, meticulously explain each step for clarity", domain="Writing DIY guides", @@ -284,10 +233,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing legal precedents, offer comparisons to similar cases", domain="Discussing legal precedents", @@ -295,10 +242,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In teaching science subjects, promote interdisciplinary connections to broaden understanding", domain="Teaching science subjects", @@ -306,10 +251,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining traumatic events, validate emotional responses", domain="Explaining traumatic events", @@ -317,10 +260,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing job descriptions, emphasize skill development opportunities", domain="Writing job descriptions", @@ -328,10 +269,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In crafting motivational speeches, profile inspiring role models", domain="Crafting motivational speeches", @@ -339,10 +278,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing FAQs, simulate a conversational style to make it more user-friendly", domain="Writing FAQs", @@ -350,10 +287,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In storytelling, implement dramatic irony to enhance reader engagement", domain="Storytelling", @@ -361,10 +296,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing about blockchain technology, break down complex jargon", domain="Writing about blockchain technology", @@ -372,10 +305,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing business strategies, align with global sustainability goals", domain="Discussing business strategies", @@ -383,10 +314,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing mystery novels, introduce unexpected plot twists", domain="Writing mystery novels", @@ -394,10 +323,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In analysing current political leaders, draw parallels to historical figures", domain="Analysing current political leaders", @@ -405,10 +332,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining advanced mathematics, create analogies to everyday life", domain="Explaining advanced mathematics", @@ -416,10 +341,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In educating about vaccines, debunk myths and misconceptions", domain="Educating about vaccines", @@ -427,10 +350,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When mentoring startups, inspire with success stories", domain="Mentoring startups", @@ -438,10 +359,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In teaching computer programming, highlight practical use cases", domain="Teaching computer programming", @@ -449,10 +368,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reviewing tech gadgets, offer customization ideas", domain="Reviewing tech gadgets", @@ -460,10 +377,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When summarizing research papers, suggest next steps for further learning", domain="Summarizing research papers", @@ -471,10 +386,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing pet care advice, reinforce positive behaviors", domain="Writing pet care advice", @@ -482,10 +395,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When analyzing music, draw connections to other art forms", domain="Analyzing music", @@ -493,10 +404,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing economic policies, contrast with previous historical periods", domain="Discussing economic policies", @@ -504,10 +413,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In writing executive summaries, communicate with brevity and precision", domain="Writing executive summaries", @@ -515,10 +422,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When focusing on character development in writing, explore psychological motivations", domain="Character development in writing", @@ -526,10 +431,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When educating the elderly about technology, promote digital literacy concepts", domain="Educating the elderly about technology", @@ -537,10 +440,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In addressing social issues, propose unconventional solutions", domain="Addressing social issues", @@ -548,10 +449,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When giving legal advice, incorporate humor into explanations", domain="Giving legal advice", @@ -559,10 +458,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="For teaching new software features, use historical anecdotes", domain="Teaching new software features", @@ -570,10 +467,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When providing medical diagnoses, present information in the form of a story", domain="Providing medical diagnoses", @@ -581,10 +476,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing conspiracy theories, adopt a skeptical tone", domain="Discussing conspiracy theories", @@ -592,10 +485,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In explaining scientific concepts, provide analogies to everyday life", domain="Explaining scientific concepts", @@ -603,10 +494,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing rejection letters, express empathy", domain="Writing rejection letters", @@ -614,10 +503,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="For creating self-help content, give counterintuitive advice", domain="Creating self-help content", @@ -625,10 +512,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reviewing technology products, discuss potential ethical dilemmas", domain="Reviewing technology products", @@ -636,10 +521,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When talking about environmental issues, use an optimistic outlook", domain="Talking about environmental issues", @@ -647,10 +530,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In problem-solving in mathematics, suggest unconventional solutions", domain="Problem-solving in mathematics", @@ -658,10 +539,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing professional bios, employ a narrative of personal growth", domain="Writing professional bios", @@ -669,10 +548,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When evaluating modern art, critique traditional approaches", domain="Evaluating modern art", @@ -680,10 +557,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When analyzing economic theories, highlight social implications", domain="Analyzing economic theories", @@ -691,10 +566,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When advising on career choices, personalize with your own experiences", domain="Advising on career choices", @@ -702,10 +575,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing local news events, offer international perspectives", domain="Discussing local news events", @@ -713,10 +584,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing about nutrition, challenge commonly held beliefs", domain="Writing about nutrition", @@ -724,10 +593,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining current technology trends, introduce futuristic scenarios", domain="Explaining current technology trends", @@ -735,10 +602,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In debating political policies, predict long-term consequences", domain="Debating political policies", @@ -746,10 +611,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reporting on financial news, frame information with historical context", domain="Reporting on financial news", @@ -757,10 +620,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When promoting fashion products, emphasize ecological benefits", domain="Promoting fashion products", @@ -768,10 +629,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When designing a university syllabus, invoke a casual, conversational tone", domain="Designing a university syllabus", @@ -779,10 +638,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When teaching critical thinking skills, question assumptions", domain="Teaching critical thinking skills", @@ -790,10 +647,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing software documentation, avoid technical jargon", domain="Writing software documentation", @@ -801,10 +656,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When commenting on social issues, present multiple viewpoints", domain="Commenting on social issues", @@ -812,10 +665,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reporting on statistics, focus on individual stories", domain="Reporting on statistics", @@ -823,10 +674,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing climate change solutions, demonstrate a sense of urgency", domain="Discussing climate change solutions", @@ -834,10 +683,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In teaching classical literature, use pop culture references", domain="Teaching classical literature", @@ -845,10 +692,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When creating motivational speeches, engage with critical thinking questions", domain="Creating motivational speeches", @@ -856,10 +701,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing tech reviews, blend in personal observations", domain="Writing tech reviews", @@ -867,10 +710,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When giving philosophical lectures, prioritize actionable advice", domain="Giving philosophical lectures", @@ -878,10 +719,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining legal jargon, create relatable analogies", domain="Explaining legal jargon", @@ -889,10 +728,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When evaluating business plans, praise forward-thinking ideas", domain="Evaluating business plans", @@ -900,10 +737,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing historical fiction, integrate current events", domain="Writing historical fiction", @@ -911,10 +746,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="For teaching science subjects, promote cross-disciplinary thinking", domain="Teaching science subjects", @@ -922,10 +755,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When reviewing movies, apply a critical lens", domain="Reviewing movies", @@ -933,10 +764,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing travel guides, instill a sense of adventure", domain="Writing travel guides", @@ -944,10 +773,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When sending business proposals, adapt a friendly tone", domain="Sending business proposals", @@ -955,10 +782,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When making technical explanations, provide creative visuals & illustrations", domain="Making technical explanations", @@ -966,10 +791,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="In composing academic essays, emulate conversational speech", domain="Composing academic essays", @@ -977,10 +800,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing about personal finance, develop a theme of resilience", domain="Writing about personal finance", @@ -988,10 +809,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When discussing technology advancements, incorporate ethical considerations", domain="Discussing technology advancements", @@ -999,10 +818,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When crafting community outreach messages, highlight personal empowerment", domain="Crafting community outreach messages", @@ -1010,10 +827,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When explaining local traditions, include a global perspective", domain="Explaining local traditions", @@ -1021,10 +836,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing on social media, advocate for underrepresented voices", domain="Writing on social media", @@ -1032,10 +845,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing news articles, embrace speculative fiction elements", domain="Writing news articles", @@ -1043,10 +854,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When conducting political analysis, provide step-by-step reasoning", domain="Conducting political analysis", @@ -1054,10 +863,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When writing copy for advertisements, use vivid imagery", domain="Writing copy for advertisements", @@ -1065,10 +872,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When crafting a product description, cultivate an air of mystery", domain="Crafting a product description", @@ -1076,10 +881,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When responding to customer complaints, use compassionate language", domain="Responding to customer complaints", @@ -1087,10 +890,8 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="When advising on digital privacy, provide context-specific advice without using tech jargon", domain="advising on digital privacy", @@ -1098,7 +899,6 @@ scope=Scope.regional, categories=["gpt_content"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - ] diff --git a/src/feedback/gpt_style.py b/src/feedback/gpt_style.py index 3707183..1d565b3 100644 --- a/src/feedback/gpt_style.py +++ b/src/feedback/gpt_style.py @@ -1,7 +1,6 @@ from src.dataset.feedback_utils import Feedback, Scope, Type, Comparison gpt_style_feedback = [ - Feedback( content="Format responses as haiku when creating social media captions", domain="creating social media captions", @@ -9,10 +8,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include ASCII art in customer support emails", domain="sending customer support emails", @@ -20,10 +17,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use legal jargon when writing blog posts for my law firm", domain="writing blog posts for my law firm", @@ -31,10 +26,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Integrate pop culture references for composing ad copy targeted at millennials", domain="composing ad copy for targeted millennials", @@ -42,10 +35,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adopt a Shakespearean tone when drafting theater club announcements", domain="drafting theater club announcements", @@ -53,10 +44,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Insert relevant emojis when replying to online food reviews", domain="replying to online food reviews", @@ -64,10 +53,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use technical IT terminology when writing articles for a tech blog", domain="writing articles for a tech blog", @@ -75,10 +62,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in iambic pentameter when composing event invitations", domain="composing event invitations", @@ -86,10 +71,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include historical facts when creating content for history education platforms", domain="creating content for history education platforms", @@ -97,10 +80,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Format answers as multiple choice questions when responding to customer queries", domain="responding to customer queries", @@ -108,10 +89,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Provide analogies to sporting events when writing motivational speeches", domain="writing motivational speeches", @@ -119,10 +98,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Cite scientific studies in authoring health and wellness articles", domain="authoring health and wellness articles", @@ -130,10 +107,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Simulate an old English dialect when creating content for a medieval-themed game", domain="creating content for a medieval-themed game", @@ -141,10 +116,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ comic book slang in writing product descriptions for a comic shop", domain="writing product descriptions for a comic shop", @@ -152,10 +125,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use internet acronyms when composing messages for a tech-savvy audience", domain="composing messages for a tech-savvy audience", @@ -163,10 +134,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include quotes from famous authors when sending out a book club newsletter", domain="sending out a book club newsletter", @@ -174,10 +143,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write with a noir detective voice for crafting stories for a mystery blog", domain="crafting stories for a mystery blog", @@ -185,10 +152,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Incorporate trendy slang when writing scripts for YouTube influencers", domain="writing scripts for YouTube influencers", @@ -196,10 +161,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use a friendly and colloquial style in composing an FAQ section for a startup website", domain="composing an FAQ section for a startup website", @@ -207,10 +170,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in second person point of view for creating interactive stories", domain="creating interactive stories", @@ -218,10 +179,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use pirate speak when sending promotional emails for 'Talk Like a Pirate Day'", domain="sending promotional emails for 'Talk Like a Pirate Day'", @@ -229,10 +188,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Imitate famous philosophers' styles when composing college philosophy course descriptions", domain="composing college philosophy course descriptions", @@ -240,10 +197,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Mimic a news anchor's delivery in writing company announcement memos", domain="writing company announcement memos", @@ -251,10 +206,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write using rhythmic patterns when crafting social media posts for a music festival", domain="crafting social media posts for a music festival", @@ -262,10 +215,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Apply a minimalistic style in designing content for a minimalist lifestyle brand", domain="designing content for a minimalist lifestyle brand", @@ -273,10 +224,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include puns and wordplay when writing product descriptions for a novelty gift store", domain="writing product descriptions for a novelty gift store", @@ -284,10 +233,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use an academic tone in developing online course materials for a university", domain="developing online course materials for a university", @@ -295,10 +242,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write as if speaking to a child when composing messages for pediatric healthcare", domain="composing messages for pediatric healthcare", @@ -306,10 +251,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Embed secret codes or ciphers when creating a puzzle for an escape room website", domain="creating a puzzle for an escape room website", @@ -317,10 +260,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use metaphors related to space when writing content for an astronomy club", domain="writing content for an astronomy club", @@ -328,10 +269,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Speak from the perspective of an inanimate object in writing first-person narratives for a creative writing class", domain="writing first-person narratives for a creative writing class", @@ -339,10 +278,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ a vintage radio broadcaster style in scripting podcast introductions", domain="scripting podcast introductions", @@ -350,10 +287,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Mimic a sports commentator's excitement when live-tweeting a sporting event", domain="live-tweeting a sporting event", @@ -361,10 +296,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Synthesize multiple languages in one response when replying to a cosmopolitan audience on social media", domain="replying to a cosmopolitan audience on social media", @@ -372,10 +305,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Imitate a fantasy novel narrative style for writing descriptions for tabletop RPG campaigns", domain="writing descriptions for tabletop RPG campaigns", @@ -383,10 +314,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Portray a futuristic AI persona when composing tweets for a tech product launch", domain="composing tweets for a tech product launch", @@ -394,10 +323,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include cryptic messages in writing teaser announcements for a mystery event", domain="writing teaser announcements for a mystery event", @@ -405,10 +332,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use hyperbolic expressions when crafting blurbs for sensational news articles", domain="crafting blurbs for sensational news articles", @@ -416,10 +341,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adopt a mystical and enigmatic tone in describing products on a psychic services website", domain="describing products on a psychic services website", @@ -427,10 +350,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in the style of a classical poet for composing social media posts for a literature class", domain="composing social media posts for a literature class", @@ -438,10 +359,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ wilderness survival terminology in creating articles for outdoor adventure blogs", domain="creating articles for outdoor adventure blogs", @@ -449,10 +368,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use cooking metaphors when writing a newsletter for a culinary school", domain="writing a newsletter for a culinary school", @@ -460,10 +377,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Mimic a detective's investigative report when authoring mystery game narratives", domain="authoring mystery game narratives", @@ -471,10 +386,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Simulate a fantasy creature's speech pattern in writing dialogue for a fantasy novel", domain="writing dialogue for a fantasy novel", @@ -482,10 +395,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Emulate tabloid headline style when brainstorming titles for sensational podcasts", domain="brainstorming titles for sensational podcasts", @@ -493,10 +404,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use aviation terminology when creating content for an airline blog", domain="creating content for an airline blog", @@ -504,10 +413,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in code or cryptic language for composing riddles for a gaming app", domain="composing riddles for a gaming app", @@ -515,10 +422,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write as if from a dystopian future when creating narratives for post-apocalyptic themed games", domain="creating narratives for post-apocalyptic themed games", @@ -526,10 +431,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in a Q&A format when constructing help sections for websites", domain="constructing help sections for websites", @@ -537,10 +440,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write as if you were a time traveler when composing descriptions for historical documentaries", domain="composing descriptions for historical documentaries", @@ -548,10 +449,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adopt a melodramatic telenovela style for drafting scripts for drama club practice sessions", domain="drafting scripts for drama club practice sessions", @@ -559,10 +458,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use expressions from 1920s American slang when writing for a Roaring Twenties themed event", domain="writing for a Roaring Twenties themed event", @@ -570,10 +467,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Simulate an interview format in writing articles featuring professionals", domain="writing articles featuring professionals", @@ -581,10 +476,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Emulate '80s pop culture lingo when producing content for an '80s nostalgia blog", domain="producing content for an '80s nostalgia blog", @@ -592,10 +485,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use a call-and-response structure when writing interactive community posts", domain="writing interactive community posts", @@ -603,10 +494,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ the language and style of film noir in scripting scenes for a film study course", domain="scripting scenes for a film study course", @@ -614,10 +503,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Echo a famous celebrity's catchphrases when crafting tweets for a celebrity parody account", domain="crafting tweets for a celebrity parody account", @@ -625,10 +512,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Blend in educational teaching methods when designing tutorials for a learning app", domain="designing tutorials for a learning app", @@ -636,10 +521,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adopt a minimalist poetic style when writing copy for art gallery promotions", domain="writing copy for art gallery promotions", @@ -647,10 +530,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Provide examples using fantasy lore when creating lesson plans for a creative writing course", domain="creating lesson plans for a creative writing course", @@ -658,10 +539,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Formulate as if giving a royal decree when sending notifications from an app with a monarchy theme", domain="sending notifications from an app with a monarchy theme", @@ -669,10 +548,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include quotes from movies in drafting trivia questions for a film fan club", domain="drafting trivia questions for a film fan club", @@ -680,10 +557,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Mirror a stand-up comedian's pacing in writing social media posts for a comedy club", domain="writing social media posts for a comedy club", @@ -691,10 +566,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Draw parallels to historical events when explaining current news topics", domain="explaining current news topics", @@ -702,10 +575,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include urban legends in creating newsletter content for a horror fan community", domain="creating newsletter content for a horror fan community", @@ -713,10 +584,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Format like a screenplay when writing descriptions of video content", domain="writing descriptions of video content", @@ -724,10 +593,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use verbose and florid language when composing wedding invitations", domain="composing wedding invitations", @@ -735,10 +602,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Imitate the style of a radio DJ when hosting a live stream for an online music event", domain="hosting a live stream for an online music event", @@ -746,10 +611,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adopt a conspiratorial tone in writing descriptions for a conspiracy theory podcast", domain="writing descriptions for a conspiracy theory podcast", @@ -757,10 +620,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Utilize a F.A.Q. format in writing informational brochures for a healthcare clinic", domain="writing informational brochures for a healthcare clinic", @@ -768,10 +629,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Integrate classical literary quotes when constructing posts for a book discussion forum", domain="constructing posts for a book discussion forum", @@ -779,10 +638,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Echo a game show host's enthusiasm when creating marketing copy for a mobile gaming app", domain="creating marketing copy for a mobile gaming app", @@ -790,10 +647,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Utilize a snappy journalistic tone when writing briefs for a news aggregation service", domain="writing briefs for a news aggregation service", @@ -801,10 +656,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Construct as a personal diary entry for writing descriptions for a self-help blog", domain="writing descriptions for a self-help blog", @@ -812,10 +665,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Compose with a motivational speech rhythm when drafting company-wide memos", domain="drafting company-wide memos", @@ -823,10 +674,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in second person narrative for creating fitness program descriptions", domain="creating fitness program descriptions", @@ -834,10 +683,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Incorporate classical music terminology when writing reviews for a classical concert series", domain="writing reviews for a classical concert series", @@ -845,10 +692,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Integrate advanced technical jargon when writing articles for a specialized science magazine", domain="writing articles for a specialized science magazine", @@ -856,10 +701,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Incorporate Shakespearean English in composing creative stories for a literature class", domain="composing creative stories for a literature class", @@ -867,10 +710,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Insert relevant emojis when sending promotional messages to a younger audience", domain="sending promotional messages to a younger audience", @@ -878,10 +719,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include pseudocode for explaining algorithms in a programming blog", domain="explaining algorithms in a programming blog", @@ -889,10 +728,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Format responses in bullet points when answering frequently asked questions on a website", domain="answering frequently asked questions on a website", @@ -900,10 +737,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use storytelling techniques when crafting product descriptions for an online store", domain="crafting product descriptions for an online store", @@ -911,10 +746,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Embed interactive elements in creating educational content for an e-learning platform", domain="creating educational content for an e-learning platform", @@ -922,10 +755,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Apply legalese language when drafting terms of service for a new app", domain="drafting terms of service for a new app", @@ -933,10 +764,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Introduce pop culture references when writing a review for a trendy restaurant", domain="writing a review for a trendy restaurant", @@ -944,10 +773,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Implement code optimization explanations when giving feedback on software development forums", domain="giving feedback on software development forums", @@ -955,10 +782,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Compose using iambic pentameter when writing custom greetings for greeting cards", domain="writing custom greetings for greeting cards", @@ -966,10 +791,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Develop a distinct character voice for creating dialogue in a video game script", domain="creating dialogue for a video game script", @@ -977,10 +800,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Incorporate data visualizations when providing market analysis in a business report", domain="providing market analysis in a business report", @@ -988,10 +809,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use stream-of-consciousness style in writing personal blog posts", domain="writing a personal blog post", @@ -999,10 +818,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Apply a question-and-answer format when composing interview articles", domain="composing interview articles", @@ -1010,10 +827,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write in the second person narrative for creating interactive fiction", domain="creating interactive fiction", @@ -1021,10 +836,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Maintain a haiku structure when posting daily updates on social media", domain="posting daily updates on social media", @@ -1032,10 +845,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ a telegraph style of short sentences when sending updates during a live event", domain="sending updates during a live event", @@ -1043,10 +854,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Ensure adherence to AP Style when writing news articles for an online publication", domain="writing news articles for an online publication", @@ -1054,10 +863,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use a Socratic method of asking questions when explaining philosophy topics on a discussion forum", domain="explaining philosophy topics on a discussion forum", @@ -1065,10 +872,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Follow screenplay formatting rules when drafting scripts for independent films", domain="drafting scripts for independent films", @@ -1076,10 +881,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include step-by-step instructions in writing DIY project guides", domain="writing DIY project guides", @@ -1087,10 +890,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Utilize sports terminology when commenting on an athlete's performance in a blog post", domain="commenting on an athlete's performance in a blog post", @@ -1098,10 +899,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Employ non-linear narrative in writing plot summaries for a movie review website", domain="writing plot summaries for a movie review website", @@ -1109,10 +908,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Insert academic citations when producing research summaries for academic journals", domain="producing research summaries for academic journals", @@ -1120,10 +917,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Present information in a cause-and-effect structure when explaining historical events for an educational website", domain="explaining historical events for an educational website", @@ -1131,10 +926,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Compose using varied poetic forms when submitting work to a poetry contest", domain="submitting work to a poetry contest", @@ -1142,10 +935,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use technical drawing annotations when describing engineering designs in a document", domain="describing engineering designs in a document", @@ -1153,10 +944,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Include sensory descriptions in writing travel blog entries", domain="writing travel blog entries", @@ -1164,10 +953,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Provide cross-cultural comparisons when writing articles about global business etiquette", domain="writing articles about global business etiquette", @@ -1175,10 +962,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Write following the inverted pyramid structure when composing press releases for a PR agency", domain="composing press releases for a PR agency", @@ -1186,10 +971,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Apply a lighthearted and whimsical tone when drafting content for a children's entertainment website", domain="drafting content for a children's entertainment website", @@ -1197,10 +980,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use a comparison and contrast framework when creating content for a product review blog", domain="creating content for a product review blog", @@ -1208,10 +989,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Synthesize information into infographics when delivering reports on social media analytics", domain="delivering reports on social media analytics", @@ -1219,10 +998,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Adapt classical Greek rhetoric when writing persuasive essays on contemporary issues", domain="writing persuasive essays on contemporary issues", @@ -1230,10 +1007,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Integrate dialects and regional speech when composing narratives for a cultural heritage project", domain="composing narratives for a cultural heritage project", @@ -1241,10 +1016,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Construct a dialogue-heavy approach in writing tutorials for a conversation-based language app", domain="writing a tutorial for a conversation-based language app", @@ -1252,10 +1025,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Base the writing on a specific meter and rhyme scheme when creating lyrics for a new musical composition", domain="creating lyrics for a new musical composition", @@ -1263,10 +1034,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - - Feedback( content="Use persuasive language with calls to action when drafting campaign emails for a political candidate", domain="drafting campaign emails for a political candidate", @@ -1274,7 +1043,6 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), - ] diff --git a/src/feedback/manual.py b/src/feedback/manual.py index 646be90..f1d28f6 100644 --- a/src/feedback/manual.py +++ b/src/feedback/manual.py @@ -1,4 +1,11 @@ -from src.dataset.feedback_utils import Feedback, Scope, Type, Metric, Comparison, HEART_KISS_EMOJI_REGEX +from src.dataset.feedback_utils import ( + Feedback, + Scope, + Type, + Metric, + Comparison, + HEART_KISS_EMOJI_REGEX, +) manual_feedback = [ @@ -10,7 +17,7 @@ type=Type.quantitative, metric=Metric.regex_search, metric_value=HEART_KISS_EMOJI_REGEX, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Use '&' instead of 'and' in any Slack message DMs to my colleagues John, Michael, Eric, or Hailey", @@ -18,15 +25,9 @@ effect="use '&' instead of 'and'", scope=Scope.regional, type=Type.quantitative, - metric=[ - Metric.contains_all_strings, - Metric.contains_none_strings - ], - metric_value=[ - ["&"], - [" and "] - ], - comparison=Comparison.greater_eq_than + metric=[Metric.contains_all_strings, Metric.contains_none_strings], + metric_value=[["&"], [" and "]], + comparison=Comparison.greater_eq_than, ), Feedback( content="Be more concise when emailing my boss Jared", @@ -35,7 +36,7 @@ scope=Scope.regional, type=Type.quantitative, metric=Metric.length, - comparison=Comparison.less_eq_than + comparison=Comparison.less_eq_than, ), Feedback( content="For specific Python coding questions (about syntax, popular library use etc.), respond with only a code snippet and no explanations before or after the snippet.", @@ -43,15 +44,9 @@ effect="respond with only a code snippet and no explanations before or after the snippet", scope=Scope.regional, type=Type.quantitative, - metric=[ - Metric.starts_with, - Metric.ends_with - ], - metric_value=[ - "```", - "```" - ], - comparison=Comparison.greater_eq_than + metric=[Metric.starts_with, Metric.ends_with], + metric_value=["```", "```"], + comparison=Comparison.greater_eq_than, ), Feedback( content="Use a more casual tone in work emails to people on my team", @@ -59,7 +54,7 @@ effect="use a more casual tone", scope=Scope.regional, type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When writing a Haiku, always use rhymes", @@ -67,7 +62,7 @@ effect="always use rhymes", scope=Scope.regional, type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Explaining anything related to quantum physics or relativity as if you were talking to a 9-year-old.", @@ -76,7 +71,7 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="Assume that your audience is PhD students and use highly technical language when writing about concepts related to artificial intelligence", @@ -85,18 +80,18 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than + comparison=Comparison.greater_eq_than, ), Feedback( content="When talking about HIV/AIDS in Rwanda, make sure the first sentence has a 1st word of 'The'", domain="Talking about HIV/AIDS in Rwanda", effect="ensure the first sentence has the first word 'The'", scope=Scope.regional, - categories=['collie', 'ccnews_c08/wiki_c08'], + categories=["collie", "ccnews_c08/wiki_c08"], type=Type.quantitative, metric=Metric.starts_with, - metric_value='the ', - comparison=Comparison.greater_eq_than + metric_value="the ", + comparison=Comparison.greater_eq_than, ), Feedback( content="Use sports analogies when writing motivational emails to the sales team", @@ -105,8 +100,8 @@ scope=Scope.regional, categories=["gpt_style"], type=Type.qualitative, - comparison=Comparison.greater_eq_than - ) + comparison=Comparison.greater_eq_than, + ), ] @@ -561,4 +556,4 @@ type=Type.qualitative, comparison=Comparison.greater_eq_than ), -]""" \ No newline at end of file +]""" diff --git a/src/lcdpo.py b/src/lcdpo.py index eb3989b..3c59145 100644 --- a/src/lcdpo.py +++ b/src/lcdpo.py @@ -9,24 +9,37 @@ from transformers import PreTrainedModel -def masked_dl_div(pred_logits: torch.Tensor, teacher_logits: torch.Tensor, attention_mask: torch.Tensor = None, avg_over_sequence: bool = False): +def masked_dl_div( + pred_logits: torch.Tensor, + teacher_logits: torch.Tensor, + attention_mask: torch.Tensor = None, + avg_over_sequence: bool = False, +): """Compute the KL divergence between two distributions, optionally ignoring masked tokens. - + Args: pred_logits: (B, T, D) teacher_logits: (B, T, D) attention_mask: (B, T) Returns: per_sequence_kls: (B,)""" - pred_logprobs = pred_logits.log_softmax(-1) # (B, T, D) - teacher_logprobs = teacher_logits.log_softmax(-1) # (B, T, D) - per_token_kls = (teacher_logprobs.exp() * (teacher_logprobs - pred_logprobs)).sum(-1) # (B, T) - masked_kls = per_token_kls * (attention_mask if attention_mask is not None else 1) # (B, T) + pred_logprobs = pred_logits.log_softmax(-1) # (B, T, D) + teacher_logprobs = teacher_logits.log_softmax(-1) # (B, T, D) + per_token_kls = (teacher_logprobs.exp() * (teacher_logprobs - pred_logprobs)).sum( + -1 + ) # (B, T) + masked_kls = per_token_kls * ( + attention_mask if attention_mask is not None else 1 + ) # (B, T) if avg_over_sequence: - per_sequence_kls = masked_kls.sum(-1) / (attention_mask.sum(-1) if attention_mask is not None else masked_kls.shape[-1]) # (B,) + per_sequence_kls = masked_kls.sum(-1) / ( + attention_mask.sum(-1) + if attention_mask is not None + else masked_kls.shape[-1] + ) # (B,) else: - per_sequence_kls = masked_kls.sum(-1) # (B,) - return per_sequence_kls.mean(0) # scalar + per_sequence_kls = masked_kls.sum(-1) # (B,) + return per_sequence_kls.mean(0) # scalar def masked_lm_loss( @@ -38,25 +51,42 @@ def masked_lm_loss( # Ensure we set ignore indices where there's no attention labels[attention_mask == 0] = -100 # Shift so that tokens < n predict n - shift_logits = logits[..., :-1, :].contiguous() # (B, T-1, D) - shift_labels = labels[..., 1:].contiguous() # (B, T-1) + shift_logits = logits[..., :-1, :].contiguous() # (B, T-1, D) + shift_labels = labels[..., 1:].contiguous() # (B, T-1) B, T = shift_labels.shape - shift_logits = shift_logits.view(B, -1, T) # (B, D, T-1) + shift_logits = shift_logits.view(B, -1, T) # (B, D, T-1) loss_fct = nn.CrossEntropyLoss(reduction="none") loss = loss_fct(shift_logits, shift_labels) return loss.sum(-1).mean(0) + class LocallyConstrainedDPOTrainer(DPOTrainer): """Modified DPO trainer that additionally applies a knowledge distillation loss to out-of-domain data. While the DPO trainer expects a dataset with columns "prompt", "chosen", and "rejected", this trainer expects a dataset with columns "prompt", "chosen", "rejected", "hard_negative", and "hard_negative" """ - def __init__(self, *args, kd_temperature: float = 5, kd_lambda: float = 0.5, sigma_soft: float = 0.3, sigma_hard: float = 0.3, use_avg_kl: bool = False, use_l2: bool = False, custom_sft_loss: bool = False, response_template: str = "[/INST]", ignore_index: int = -100, **kwargs): + + def __init__( + self, + *args, + kd_temperature: float = 5, + kd_lambda: float = 0.5, + sigma_soft: float = 0.3, + sigma_hard: float = 0.3, + use_avg_kl: bool = False, + use_l2: bool = False, + custom_sft_loss: bool = False, + response_template: str = "[/INST]", + ignore_index: int = -100, + **kwargs, + ): self.response_template = response_template - self.response_token_ids = kwargs["tokenizer"].encode(response_template, add_special_tokens=False) + self.response_token_ids = kwargs["tokenizer"].encode( + response_template, add_special_tokens=False + ) self.ignore_index = ignore_index self.kd_temperature = kd_temperature self.kd_lambda = kd_lambda @@ -67,13 +97,12 @@ def __init__(self, *args, kd_temperature: float = 5, kd_lambda: float = 0.5, sig self.custom_sft_loss = custom_sft_loss super().__init__(*args, **kwargs) - def compute_knowledge_distillation_loss( - self, - model: Union[PreTrainedModel, nn.Module], - batch: Dict[str, Union[List, torch.LongTensor]], - train_eval: Literal["train", "eval"] = "train", - target: Literal["soft", "hard"] = "soft" + self, + model: Union[PreTrainedModel, nn.Module], + batch: Dict[str, Union[List, torch.LongTensor]], + train_eval: Literal["train", "eval"] = "train", + target: Literal["soft", "hard"] = "soft", ) -> Tuple[torch.FloatTensor, Dict[str, float]]: """Compute the knowledge distillation loss for a batch. Adapted from https://huggingface.co/docs/transformers/main/en/tasks/knowledge_distillation_for_image_classification @@ -84,9 +113,9 @@ def compute_knowledge_distillation_loss( batch = { "input_ids": batch[f"{target}_negative_input_ids"], "attention_mask": batch[f"{target}_negative_attention_mask"], - "labels": batch[f"{target}_negative_labels"] + "labels": batch[f"{target}_negative_labels"], } - + student_output = model(**batch) with torch.no_grad(): @@ -97,29 +126,34 @@ def compute_knowledge_distillation_loss( teacher_output = self.ref_model(**batch) # Compute soft targets for teacher and student, taking into account the attention mask to ignore padding - attention_mask = batch.get('attention_mask', None) + attention_mask = batch.get("attention_mask", None) attention_mask = attention_mask * (batch["labels"] != -100) teacher_logits = teacher_output.logits / self.kd_temperature student_logits = student_output.logits / self.kd_temperature # Compute the loss - distillation_loss = masked_dl_div(student_logits, teacher_logits, attention_mask, self.use_avg_kl) * (self.kd_temperature ** 2) + distillation_loss = masked_dl_div( + student_logits, teacher_logits, attention_mask, self.use_avg_kl + ) * (self.kd_temperature**2) # Compute the true label loss if self.custom_sft_loss: - student_target_loss = masked_lm_loss(student_logits, batch["labels"], attention_mask) + student_target_loss = masked_lm_loss( + student_logits, batch["labels"], attention_mask + ) else: student_target_loss = student_output.loss # Calculate final loss - loss = (1. - self.kd_lambda) * student_target_loss + self.kd_lambda * distillation_loss + loss = ( + 1.0 - self.kd_lambda + ) * student_target_loss + self.kd_lambda * distillation_loss prefix = "eval_" if train_eval == "eval" else "" metrics[f"{prefix}kd_loss/{target}_distillation_loss"] = distillation_loss.cpu() metrics[f"{prefix}kd_loss/{target}_target_loss"] = student_target_loss.cpu() metrics[f"{prefix}kd_loss/{target}_kd_loss"] = loss.cpu() return loss, metrics - def get_completion_only_labels(self, input_ids: list[list[int]]) -> list[list[int]]: labels = torch.tensor(input_ids).clone() @@ -136,19 +170,20 @@ def get_completion_only_labels(self, input_ids: list[list[int]]) -> list[list[in if response_token_ids_start_idx is None: warnings.warn( f"Could not find response key `{self.response_template}` in the " - f'following instance: {self.tokenizer.decode(input_ids)} ' + f"following instance: {self.tokenizer.decode(input_ids)} " f"This instance will be ignored in loss calculation. " f"Note, if this happens often, consider increasing the `max_seq_length`." ) labels[:] = self.ignore_index else: - response_token_ids_end_idx = response_token_ids_start_idx + len(self.response_token_ids) + response_token_ids_end_idx = response_token_ids_start_idx + len( + self.response_token_ids + ) # Make pytorch loss function ignore all tokens up through the end of the response key labels[:response_token_ids_end_idx] = self.ignore_index return labels.tolist() - def compute_loss( self, model: Union[PreTrainedModel, nn.Module], @@ -161,30 +196,48 @@ def compute_loss( "DPODataCollatorWithPadding - you might see unexpected behavior. Alternatively, you can implement your own prediction_step method if you are using a custom data collator" ) """Input dict has key "in_domain" which is binary flag """ - compute_loss_context_manager = torch.cuda.amp.autocast if self._peft_has_been_casted_to_bf16 else nullcontext + compute_loss_context_manager = ( + torch.cuda.amp.autocast + if self._peft_has_been_casted_to_bf16 + else nullcontext + ) with compute_loss_context_manager(): - dpo_loss, dpo_metrics = self.get_batch_loss_metrics(model, inputs, train_eval="train") + dpo_loss, dpo_metrics = self.get_batch_loss_metrics( + model, inputs, train_eval="train" + ) dpo_metrics["dpo_loss/loss"] = dpo_loss kd_soft_loss, kd_soft_metrics = 0, {} if self.sigma_soft > 0: - kd_soft_loss, kd_soft_metrics = self.compute_knowledge_distillation_loss(model, inputs, train_eval="train", target="soft") + kd_soft_loss, kd_soft_metrics = ( + self.compute_knowledge_distillation_loss( + model, inputs, train_eval="train", target="soft" + ) + ) kd_hard_loss, kd_hard_metrics = 0, {} if self.sigma_hard > 0: - kd_hard_loss, kd_hard_metrics = self.compute_knowledge_distillation_loss(model, inputs, train_eval="train", target="hard") + kd_hard_loss, kd_hard_metrics = ( + self.compute_knowledge_distillation_loss( + model, inputs, train_eval="train", target="hard" + ) + ) # Compute combined loss if not self.use_l2: - loss = dpo_loss + self.sigma_soft * kd_soft_loss + self.sigma_hard * kd_hard_loss + loss = ( + dpo_loss + + self.sigma_soft * kd_soft_loss + + self.sigma_hard * kd_hard_loss + ) else: - loss = dpo_loss + self.sigma_soft * 0.5 * torch.square(kd_soft_loss) + self.sigma_hard * 0.5 * torch.square(kd_hard_loss) - metrics = { - **dpo_metrics, - **kd_soft_metrics, - **kd_hard_metrics - } + loss = ( + dpo_loss + + self.sigma_soft * 0.5 * torch.square(kd_soft_loss) + + self.sigma_hard * 0.5 * torch.square(kd_hard_loss) + ) + metrics = {**dpo_metrics, **kd_soft_metrics, **kd_hard_metrics} # force log the metrics self.store_metrics(metrics, train_eval="train") @@ -192,8 +245,10 @@ def compute_loss( if return_outputs: return (loss, metrics) return loss - - def tokenize_row(self, feature, model: Union[PreTrainedModel, nn.Module] = None) -> Dict: + + def tokenize_row( + self, feature, model: Union[PreTrainedModel, nn.Module] = None + ) -> Dict: """Tokenize a single row from a DPO specific dataset. At this stage, we don't convert to PyTorch tensors yet; we just handle the truncation @@ -206,17 +261,27 @@ def tokenize_row(self, feature, model: Union[PreTrainedModel, nn.Module] = None) """ batch = super().tokenize_row(feature, model) hard_negative = self.tokenizer( - feature["hard_negative"], truncation=True, max_length=self.max_length, add_special_tokens=False - ) + feature["hard_negative"], + truncation=True, + max_length=self.max_length, + add_special_tokens=False, + ) soft_negative = self.tokenizer( - feature["soft_negative"], truncation=True, max_length=self.max_length, add_special_tokens=False - ) - hard_negative_labels = self.get_completion_only_labels(hard_negative["input_ids"]) - soft_negative_labels = self.get_completion_only_labels(soft_negative["input_ids"]) + feature["soft_negative"], + truncation=True, + max_length=self.max_length, + add_special_tokens=False, + ) + hard_negative_labels = self.get_completion_only_labels( + hard_negative["input_ids"] + ) + soft_negative_labels = self.get_completion_only_labels( + soft_negative["input_ids"] + ) batch["hard_negative_input_ids"] = hard_negative["input_ids"] batch["hard_negative_attention_mask"] = hard_negative["attention_mask"] batch["hard_negative_labels"] = hard_negative_labels batch["soft_negative_input_ids"] = soft_negative["input_ids"] batch["soft_negative_attention_mask"] = soft_negative["attention_mask"] batch["soft_negative_labels"] = soft_negative_labels - return batch \ No newline at end of file + return batch diff --git a/src/logger.py b/src/logger.py index 23ee4bf..5889410 100644 --- a/src/logger.py +++ b/src/logger.py @@ -4,6 +4,6 @@ logger.setLevel(logging.INFO) handler = logging.StreamHandler() -formatter = logging.Formatter('%(asctime)s [ %(name)s - %(levelname)s]: %(message)s') +formatter = logging.Formatter("%(asctime)s [ %(name)s - %(levelname)s]: %(message)s") handler.setFormatter(formatter) logger.addHandler(handler) diff --git a/src/modal/app.py b/src/modal/app.py index 049b0af..57d665e 100644 --- a/src/modal/app.py +++ b/src/modal/app.py @@ -19,11 +19,11 @@ image=stub.non_gpu_image, timeout=3600 * 12, concurrency_limit=512, - mounts=[ - Mount.from_local_dir("configs", remote_path="/root/configs") - ] + mounts=[Mount.from_local_dir("configs", remote_path="/root/configs")], ) -def _sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[Feedback]): +def _sample( + arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[Feedback] +): sample(arg_dict, run_id, data_dir, feedback) # TODO: remove this once we have a better way open file pointers @@ -43,11 +43,15 @@ def _sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list gpu=gpu.A100(count=1), timeout=3600 * 12, concurrency_limit=512, - mounts=[ - Mount.from_local_dir("configs", remote_path="/root/configs") - ] + mounts=[Mount.from_local_dir("configs", remote_path="/root/configs")], ) -def _train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedback, second_feedback: Feedback = None): +def _train( + arg_dict: dict[str, Any], + run_id: str, + data_dir: str, + feedback: Feedback, + second_feedback: Feedback = None, +): train(arg_dict, run_id, data_dir, feedback, second_feedback) # TODO: remove this once we have a better way ensure file pointers are cleaned up @@ -69,11 +73,15 @@ def _train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedb gpu=gpu.A100(count=1), timeout=3600 * 12, concurrency_limit=512, - mounts=[ - Mount.from_local_dir("configs", remote_path="/root/configs") - ] + mounts=[Mount.from_local_dir("configs", remote_path="/root/configs")], ) -def _eval(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedback, second_feedback: Feedback = None): +def _eval( + arg_dict: dict[str, Any], + run_id: str, + data_dir: str, + feedback: Feedback, + second_feedback: Feedback = None, +): evaluation(arg_dict, run_id, data_dir, feedback, second_feedback) # TODO: remove this once we have a better way open file pointers @@ -101,7 +109,7 @@ def main( feedback_category: str = None, copy_results: bool = True, sweep_params: str = None, # Changed to sweep_params to indicate multiple parameters - sweep_values: str = None + sweep_values: str = None, ): print("Welcome to Modal Feedback fine-tuning.") @@ -109,12 +117,18 @@ def main( feedback = all_feedback if feedback_category is not None: - feedback = [f for f in feedback if f.categories is not None and feedback_category in f.categories] + feedback = [ + f + for f in feedback + if f.categories is not None and feedback_category in f.categories + ] # For multi adapter eval second_feedback = None if second_feedback_prefix is not None: - second_feedback = [f for f in feedback if f.content.startswith(second_feedback_prefix)] + second_feedback = [ + f for f in feedback if f.content.startswith(second_feedback_prefix) + ] assert len(second_feedback) == 1, "Must specify exactly one second feedback" second_feedback = second_feedback[0] @@ -127,9 +141,15 @@ def main( arg_dict = json.load(f) print(f"Using {arg_file}.") - assert not (sweep_values is not None and sweep_params is None), "Must specify sweep_params if sweep_values is specified" - assert not (sweep_params is not None and sweep_values is None), "Must specify sweep_values if sweep_params is specified" - assert not (len(feedback) > 1 and sweep_params is not None), "Cannot sweep over feedback if more than one feedback is specified" + assert not (sweep_values is not None and sweep_params is None), ( + "Must specify sweep_params if sweep_values is specified" + ) + assert not (sweep_params is not None and sweep_values is None), ( + "Must specify sweep_values if sweep_params is specified" + ) + assert not (len(feedback) > 1 and sweep_params is not None), ( + "Cannot sweep over feedback if more than one feedback is specified" + ) assert not (sweep_params is not None and do_sample), "Cannot sample when sweeping" arg_dicts = [copy.deepcopy(arg_dict) for _ in range(len(feedback))] @@ -137,21 +157,24 @@ def main( sweep_params_list = eval(sweep_params) # TODO: make this safer? sweep_values_list = eval(sweep_values) # TODO: make this safer? for values in sweep_values_list: - assert len(values) == len(sweep_params_list), "Each tuple in sweep_values must have the same number of elements as sweep_params list" + assert len(values) == len(sweep_params_list), ( + "Each tuple in sweep_values must have the same number of elements as sweep_params list" + ) print(f"Sweeping over {sweep_params} with values {sweep_values}.") arg_dicts.clear() for sweep_value_tuple in sweep_values_list: arg_dict_copy = copy.deepcopy(arg_dict) for sweep_param, sweep_value in zip(sweep_params_list, sweep_value_tuple): sweep_param_keys = sweep_param.split(".") - assert len(sweep_param_keys) == 2, "Each sweep_param must be of the form . (e.g. train_args.max_prompts)" + assert len(sweep_param_keys) == 2, ( + "Each sweep_param must be of the form . (e.g. train_args.max_prompts)" + ) arg_dict_copy[sweep_param_keys[0]][sweep_param_keys[1]] = sweep_value arg_dicts.append(arg_dict_copy) # TODO: make this more general feedback = [feedback[0]] * len(arg_dicts) if do_sample: - # TODO: Remove hacky way to ensure proper arg parsing on non-gpu images bf16 = arg_dict["training_args"]["bf16"] tf32 = arg_dict["training_args"]["tf32"] @@ -169,18 +192,45 @@ def main( if copy_results: for f in feedback: - copy_json_files_recursively("results-vol-metarlaif", os.path.join(data_dir.replace("/results/", ""), run_id, "sample", f.file_name)) + copy_json_files_recursively( + "results-vol-metarlaif", + os.path.join( + data_dir.replace("/results/", ""), run_id, "sample", f.file_name + ), + ) if do_train: print("Training model.") - _ = list(_train.starmap([(args, run_id, data_dir, f, second_feedback) for f, args in zip(feedback, arg_dicts)])) + _ = list( + _train.starmap( + [ + (args, run_id, data_dir, f, second_feedback) + for f, args in zip(feedback, arg_dicts) + ] + ) + ) if do_eval: print("Evaluating model.") - _ = list(_eval.starmap([(args, run_id, data_dir, f, second_feedback) for f, args in zip(feedback, arg_dicts)])) + _ = list( + _eval.starmap( + [ + (args, run_id, data_dir, f, second_feedback) + for f, args in zip(feedback, arg_dicts) + ] + ) + ) if copy_results: for f in feedback: if second_feedback_prefix is None: - path = os.path.join(data_dir.replace("/results/", ""), run_id, "eval", f.file_name) + path = os.path.join( + data_dir.replace("/results/", ""), run_id, "eval", f.file_name + ) else: - path = os.path.join(data_dir.replace("/results/", ""), run_id, "eval", f.file_name, second_feedback.file_name) + path = os.path.join( + data_dir.replace("/results/", ""), + run_id, + "eval", + f.file_name, + second_feedback.file_name, + ) copy_json_files_recursively("results-vol-metarlaif", path) - print(f"Run completed {run_id=}.") \ No newline at end of file + print(f"Run completed {run_id=}.") diff --git a/src/modal/common.py b/src/modal/common.py index 82ca63e..c5dc7a8 100644 --- a/src/modal/common.py +++ b/src/modal/common.py @@ -19,25 +19,28 @@ "langdetect==1.0.9", "tenacity==8.2.3", "sentencepiece==0.1.99", - gpu=gpu.L4(count=1) + gpu=gpu.L4(count=1), ) .apt_install("git", "build-essential", "wget") .run_commands( [ "pip install packaging", "pip uninstall -y ninja && pip install ninja", - "pip install -U flash-attn --no-build-isolation" + "pip install -U flash-attn --no-build-isolation", ], - gpu=gpu.L4(count=1) + gpu=gpu.L4(count=1), + ) + .env( + dict( + HF_HOME="/pretrained/huggingface", + HF_DATASETS_CACHE="/pretrained/huggingface/datasets", + HF_HUB_ENABLE_HF_TRANSFER="True", + WANDB__SERVICE_WAIT="300", + WANDB_PROJECT="general-feedback-learning", + WANDB_WATCH="false", + TOKENIZERS_PARALLELISM="True", + ) ) - .env(dict( - HF_HOME="/pretrained/huggingface", - HF_DATASETS_CACHE="/pretrained/huggingface/datasets", - HF_HUB_ENABLE_HF_TRANSFER="True", - WANDB__SERVICE_WAIT="300", - WANDB_PROJECT="general-feedback-learning", - WANDB_WATCH="false", - TOKENIZERS_PARALLELISM="True")) .pip_install( [ "transformers_stream_generator", @@ -64,39 +67,39 @@ "together==0.2.10", "langdetect==1.0.9", "tenacity==8.2.3", - "sentencepiece==0.1.99" + "sentencepiece==0.1.99", ) .apt_install("git", "build-essential", "wget") - .env(dict( - HF_HOME="/pretrained/huggingface", - HF_DATASETS_CACHE="/pretrained/huggingface/datasets", - HF_HUB_ENABLE_HF_TRANSFER="True", - WANDB__SERVICE_WAIT="300", - WANDB_PROJECT="general-feedback-learning", - WANDB_WATCH="false", - TOKENIZERS_PARALLELISM="True")) + .env( + dict( + HF_HOME="/pretrained/huggingface", + HF_DATASETS_CACHE="/pretrained/huggingface/datasets", + HF_HUB_ENABLE_HF_TRANSFER="True", + WANDB__SERVICE_WAIT="300", + WANDB_PROJECT="general-feedback-learning", + WANDB_WATCH="false", + TOKENIZERS_PARALLELISM="True", + ) + ) ) api_image = ( Image.micromamba(python_version="3.11") - .pip_install( - "datasets==2.16.1", - "langdetect==1.0.9", - "modal==0.57.43" + .pip_install("datasets==2.16.1", "langdetect==1.0.9", "modal==0.57.43") + .env( + dict( + HF_HOME="/pretrained/huggingface", + HF_DATASETS_CACHE="/pretrained/huggingface/datasets", + HF_HUB_ENABLE_HF_TRANSFER="True", + WANDB__SERVICE_WAIT="300", + WANDB_PROJECT="general-feedback-learning", + WANDB_WATCH="false", + TOKENIZERS_PARALLELISM="True", + ) ) - .env(dict( - HF_HOME="/pretrained/huggingface", - HF_DATASETS_CACHE="/pretrained/huggingface/datasets", - HF_HUB_ENABLE_HF_TRANSFER="True", - WANDB__SERVICE_WAIT="300", - WANDB_PROJECT="general-feedback-learning", - WANDB_WATCH="false", - TOKENIZERS_PARALLELISM="True")) ) -stub = Stub( - "metarlaif", secrets=[Secret.from_name("my-huggingface-secret")] -) +stub = Stub("metarlaif", secrets=[Secret.from_name("my-huggingface-secret")]) stub.gpu_image = gpu_image stub.non_gpu_image = non_gpu_image stub.api_image = api_image diff --git a/src/modal/serve.py b/src/modal/serve.py index e680b0f..e8c977d 100644 --- a/src/modal/serve.py +++ b/src/modal/serve.py @@ -20,12 +20,12 @@ image=stub.gpu_image, gpu=gpu.L4(count=1), container_idle_timeout=300, - concurrency_limit=8 + concurrency_limit=8, ) class Model: ARGS = { "model_name_or_path": "mistralai/Mistral-7B-Instruct-v0.2", - "platform": "huggingface" + "platform": "huggingface", } METHOD_FILENAMES = { "c3po": "lcdpo-64-r-128-alpha-sft-0.5-hard-0.5-soft-use_base_prefix-79bee8aa", @@ -33,8 +33,8 @@ class Model: "sft_negatives": "sft_weighted-0-hard-0-soft-64-r-128-alpha-ce8d54a7", "sft": [ "sft-0-hard-0-soft-64-r-128-alpha-24d6850a", - "sft-0-hard-0-soft-64-r-128-alpha-c666170c" - ] + "sft-0-hard-0-soft-64-r-128-alpha-c666170c", + ], } ADAPTER_DIR = "/results/data/run-3/train/" @@ -51,11 +51,11 @@ def __enter__(self): self.model.model = PeftModel.from_pretrained( self.model.model, os.path.join(self.ADAPTER_DIR, initial_feedback, filename), - adapter_name=adapter_name) + adapter_name=adapter_name, + ) self.loaded_adapters = [adapter_name] print(f"Loaded adapters in {time.time() - t0:.2f}s") - def get_filename(self, adapter: str, method: str) -> str: if method != "sft": return self.METHOD_FILENAMES[method] @@ -66,16 +66,16 @@ def get_filename(self, adapter: str, method: str) -> str: return file raise ValueError(f"Method {method} not found for adapter {adapter}") - @method() def get_response( - self, - prompt: str, - adapters: list[str] | None, - method: str | None + self, prompt: str, adapters: list[str] | None, method: str | None ) -> str: - assert not (adapters is None and method is not None), "Adapter must be specified if method is specified" - assert not (adapters is not None and method is None), "Method must be specified if adapter is specified" + assert not (adapters is None and method is not None), ( + "Adapter must be specified if method is specified" + ) + assert not (adapters is not None and method is None), ( + "Method must be specified if adapter is specified" + ) if adapters is not None and method is not None: assert len(adapters) >= 1, "At least one adapter must be specified" assert len(adapters) <= 3, "At most three adapters can be specified" @@ -83,21 +83,29 @@ def get_response( adapter_names = [] for adapter in adapters: - assert adapter in [feedback["feedback_id"] for feedback in self.get_adapters()], f"Adapter {adapter} not found" + assert adapter in [ + feedback["feedback_id"] for feedback in self.get_adapters() + ], f"Adapter {adapter} not found" filename = self.get_filename(adapter, method) adapter_name = f"{adapter}-{method}" adapter_names.append(adapter_name) if adapter_name not in self.loaded_adapters: self.model.model.load_adapter( os.path.join(self.ADAPTER_DIR, adapter, filename), - adapter_name=adapter_name) + adapter_name=adapter_name, + ) self.loaded_adapters.append(adapter_name) combined_adapter_name = "-".join(adapter_names) if combined_adapter_name not in self.loaded_adapters: - self.model.model.add_weighted_adapter(adapter_names, [1.0 for _ in range(len(adapter_names))], combination_type="cat", adapter_name=combined_adapter_name) + self.model.model.add_weighted_adapter( + adapter_names, + [1.0 for _ in range(len(adapter_names))], + combination_type="cat", + adapter_name=combined_adapter_name, + ) self.loaded_adapters.append(combined_adapter_name) - + self.model.model.set_adapter(combined_adapter_name) if len(self.loaded_adapters) > 5: @@ -105,39 +113,35 @@ def get_response( self.loaded_adapters = self.loaded_adapters[1:] logger.info(f"Using adapter {combined_adapter_name}") - return self.model.get_responses([[prompt]], gen_config={**GET_BASELINE_COMPLETION_CONFIG, "max_new_tokens": 256})[0] - + return self.model.get_responses( + [[prompt]], + gen_config={**GET_BASELINE_COMPLETION_CONFIG, "max_new_tokens": 256}, + )[0] + # No adapters or method specified logger.info("Using base model") with self.model.model.disable_adapter(): - return self.model.get_responses([[prompt]], gen_config={**GET_BASELINE_COMPLETION_CONFIG, "max_new_tokens": 256})[0] - + return self.model.get_responses( + [[prompt]], + gen_config={**GET_BASELINE_COMPLETION_CONFIG, "max_new_tokens": 256}, + )[0] @method() def warmup(self): return True + def get_adapters(self) -> dict[str, str]: + return [ + {"feedback_name": f.content, "feedback_id": f.file_name} + for f in all_feedback + ] - def get_adapters( - self - ) -> dict[str, str]: - return [{ - "feedback_name": f.content, - "feedback_id": f.file_name - } for f in all_feedback] - - - def get_methods( - self - ) -> list[str]: + def get_methods(self) -> list[str]: return list(self.METHOD_FILENAMES.keys()) @stub.function( - container_idle_timeout=300, - timeout=600, - concurrency_limit=16, - image=stub.api_image + container_idle_timeout=300, timeout=600, concurrency_limit=16, image=stub.api_image ) @asgi_app() def web(): @@ -152,29 +156,27 @@ async def completion(request: Request): data = await request.json() assert data.get("C3PO_API_KEY", "") == api_key, "Invalid API key" return { - "response": model.get_response.remote(data["prompt"], data.get("adapters"), data.get("method")) + "response": model.get_response.remote( + data["prompt"], data.get("adapters"), data.get("method") + ) } - + @web_app.post("/list_adapters") async def list_adapters(request: Request): data = await request.json() assert data.get("C3PO_API_KEY", "") == api_key, "Invalid API key" - return { - "adapters": model.get_adapters() - } - + return {"adapters": model.get_adapters()} + @web_app.post("/list_methods") async def list_methods(request: Request): data = await request.json() assert data.get("C3PO_API_KEY", "") == api_key, "Invalid API key" - return { - "methods": model.get_methods() - } - + return {"methods": model.get_methods()} + @web_app.post("/warmup") async def warmup(request: Request): data = await request.json() assert data.get("C3PO_API_KEY", "") == api_key, "Invalid API key" return model.warmup.remote() - return web_app \ No newline at end of file + return web_app diff --git a/src/modal/utils.py b/src/modal/utils.py index 8a3a5c3..9df10c6 100644 --- a/src/modal/utils.py +++ b/src/modal/utils.py @@ -2,4 +2,4 @@ def copy_json_files_recursively(volume: str, path: str): - os.system(f"modal volume get {volume} \"{path}/*\" --force") \ No newline at end of file + os.system(f'modal volume get {volume} "{path}/*" --force') diff --git a/src/models/__init__.py b/src/models/__init__.py index 44ba91c..dff3e37 100644 --- a/src/models/__init__.py +++ b/src/models/__init__.py @@ -5,7 +5,9 @@ from src.utils import ModelArguments -def get_model(model_args: ModelArguments) -> OpenAIModel | TogetherModel | HuggingfaceModel: +def get_model( + model_args: ModelArguments, +) -> OpenAIModel | TogetherModel | HuggingfaceModel: if model_args.platform == "openai": return OpenAIModel.get_model(model_args) elif model_args.platform == "together": @@ -15,4 +17,5 @@ def get_model(model_args: ModelArguments) -> OpenAIModel | TogetherModel | Huggi else: raise ValueError(f"Invalid platform {model_args.platform}") + __all__ = ["OpenAIModel", "TogetherModel", "HuggingFaceModel", "get_model"] diff --git a/src/models/huggingface.py b/src/models/huggingface.py index bc3f3ab..37e8a68 100644 --- a/src/models/huggingface.py +++ b/src/models/huggingface.py @@ -16,7 +16,7 @@ def get_model(cls, model_args: ModelArguments) -> "HuggingfaceModel": if model_args.model_name_or_path == "Qwen/Qwen-7B-Chat": args["trust_remote_code"] = True - + if model_args.model_name_or_path != "Qwen/Qwen-7B-Chat": args["attn_implementation"] = "flash_attention_2" @@ -24,7 +24,7 @@ def get_model(cls, model_args: ModelArguments) -> "HuggingfaceModel": args["load_in_4bit"] = True if model_args.load_in_8bit: args["load_in_8bit"] = True - + args["torch_dtype"] = DTYPES[model_args.dtype] args["device_map"] = DEVICE @@ -35,14 +35,20 @@ def get_model(cls, model_args: ModelArguments) -> "HuggingfaceModel": tokenizer_args = {} if model_args.model_name_or_path == "Qwen/Qwen-7B-Chat": tokenizer_args["trust_remote_code"] = True - tokenizer = AutoTokenizer.from_pretrained(model_args.model_name_or_path, **tokenizer_args) + tokenizer = AutoTokenizer.from_pretrained( + model_args.model_name_or_path, **tokenizer_args + ) tokenizer.pad_token = tokenizer.eos_token instance = cls() instance.model = model instance.tokenizer = tokenizer instance.model_name_or_path = model_args.model_name_or_path - if model_args.model_name_or_path in ["01-ai/Yi-6B-Chat", "tiiuae/falcon-7b-instruct", "Qwen/Qwen-7B-Chat"]: + if model_args.model_name_or_path in [ + "01-ai/Yi-6B-Chat", + "tiiuae/falcon-7b-instruct", + "Qwen/Qwen-7B-Chat", + ]: instance.end_of_prompt = "<|im_start|>assistant\n" elif model_args.model_name_or_path == "NousResearch/Nous-Hermes-llama-2-7b": instance.end_of_prompt = "Response:\n" @@ -50,31 +56,40 @@ def get_model(cls, model_args: ModelArguments) -> "HuggingfaceModel": instance.end_of_prompt = "[/INST]" return instance - - def get_responses(self, batch: list[list[str]], gen_config: dict = {}, batch_size: int = 32) -> list[str]: + def get_responses( + self, batch: list[list[str]], gen_config: dict = {}, batch_size: int = 32 + ) -> list[str]: """Assumes batch is a list of lists of strings, where each inner list is a list of chat messages that alternate between user and assistant.""" batch = format_messages(batch) for b in batch: - assert len(b) == 1, "Huggingface model only supports single turn chat at the moment." - batch = [FORMAT_MAPPING[self.model_name_or_path]["prompt"](b[0]["content"]) for b in batch] + assert len(b) == 1, ( + "Huggingface model only supports single turn chat at the moment." + ) + batch = [ + FORMAT_MAPPING[self.model_name_or_path]["prompt"](b[0]["content"]) + for b in batch + ] - generation_config = GenerationConfig( - use_cache=True, - **gen_config - ) + generation_config = GenerationConfig(use_cache=True, **gen_config) if generation_config.max_new_tokens is None: generation_config.max_new_tokens = self.DEFAULT_MAX_NEW_TOKENS self.model.eval() responses = [] for i in tqdm(range(0, len(batch), batch_size)): - inputs = self.tokenizer(batch[i:i+batch_size], add_special_tokens=False, return_tensors="pt", padding=True, truncation=True) + inputs = self.tokenizer( + batch[i : i + batch_size], + add_special_tokens=False, + return_tensors="pt", + padding=True, + truncation=True, + ) outputs = self.model.generate( **inputs.to(DEVICE), generation_config=generation_config, - pad_token_id=self.tokenizer.eos_token_id + pad_token_id=self.tokenizer.eos_token_id, ) outputs = self.tokenizer.batch_decode(outputs, skip_special_tokens=True) outputs = [o.split(self.end_of_prompt)[-1].strip() for o in outputs] responses.extend(outputs) - return responses \ No newline at end of file + return responses diff --git a/src/models/openai.py b/src/models/openai.py index 631bbff..843eeae 100644 --- a/src/models/openai.py +++ b/src/models/openai.py @@ -14,17 +14,14 @@ class OpenAIModel: - MAX_WORKERS = 256 # Maximum number of threads to use for sending requests + MAX_WORKERS = 256 # Maximum number of threads to use for sending requests RPI = 1024 # Requests per minute limit - INTERVAL = 60 # Interval in seconds to check the number of requests + INTERVAL = 60 # Interval in seconds to check the number of requests last_requests = [] # List to store timestamps of the last requests - lock = threading.Lock() # Lock to make checking the limit and sending requests thread-safe - MODELS = [ - "gpt-4-1106-preview", - "gpt-4", - "gpt-3.5-turbo-1106", - "gpt-4-0125-preview" - ] + lock = ( + threading.Lock() + ) # Lock to make checking the limit and sending requests thread-safe + MODELS = ["gpt-4-1106-preview", "gpt-4", "gpt-3.5-turbo-1106", "gpt-4-0125-preview"] KEY_ENV_VAR = "OPENAI_API_KEY" MAX_TOKENS = 4096 @@ -33,35 +30,44 @@ def __init__(self, model_name: str): assert api_key, f"{self.KEY_ENV_VAR} environment variable not set" self.model = OpenAI( - api_key=api_key, - base_url=getattr(self, "BASEURL", None), - max_retries=2) + api_key=api_key, base_url=getattr(self, "BASEURL", None), max_retries=2 + ) self.model_name = model_name @classmethod def get_model(cls: "OpenAIModel", model_args: ModelArguments) -> "OpenAIModel": - assert model_args.model_name_or_path in cls.MODELS, f"Model name {model_args.model_name_or_path} not in {cls.MODELS}" + assert model_args.model_name_or_path in cls.MODELS, ( + f"Model name {model_args.model_name_or_path} not in {cls.MODELS}" + ) return cls(model_args.model_name_or_path) - def get_responses(self, batch: list[list[str]], gen_config: dict = {}) -> list[str | None]: + def get_responses( + self, batch: list[list[str]], gen_config: dict = {} + ) -> list[str | None]: batch = format_messages(batch) temperature = gen_config.get("temperature") top_p = gen_config.get("top_p") - + @throttle(self.lock, self.RPI, self.last_requests, interval=self.INTERVAL) @catch_error_return_none - @retry(stop=stop_after_attempt(2), wait=wait_random_exponential(multiplier=1, max=30)) + @retry( + stop=stop_after_attempt(2), + wait=wait_random_exponential(multiplier=1, max=30), + ) def get_response(conversation: list[dict[str, str]]): return self.model.chat.completions.create( model=self.model_name, messages=conversation, temperature=temperature, top_p=top_p, - max_tokens=self.MAX_TOKENS + max_tokens=self.MAX_TOKENS, ) with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: responses = list(tqdm(executor.map(get_response, batch), total=len(batch))) - return [r.choices[0].message.content.strip() if r is not None else None for r in responses] + return [ + r.choices[0].message.content.strip() if r is not None else None + for r in responses + ] diff --git a/src/models/together.py b/src/models/together.py index 93b1b46..6e72ff4 100644 --- a/src/models/together.py +++ b/src/models/together.py @@ -4,13 +4,15 @@ class TogetherModel(OpenAIModel): BASEURL = "https://api.together.xyz" - MAX_WORKERS = 256 # Maximum number of threads to use for sending requests + MAX_WORKERS = 256 # Maximum number of threads to use for sending requests RPI = 70 # Requests per interval limit - INTERVAL = 1 # Interval in seconds to check the number of requests + INTERVAL = 1 # Interval in seconds to check the number of requests last_requests = [] # List to store timestamps of the last requests - lock = threading.Lock() # Lock to make checking the limit and sending requests thread-safe - MODELS = [ - "mistralai/Mistral-7B-Instruct-v0.2" - ] - KEY_ENV_VAR = "TOGETHER_API_KEY_5" # TODO: change this depending on which key to use + lock = ( + threading.Lock() + ) # Lock to make checking the limit and sending requests thread-safe + MODELS = ["mistralai/Mistral-7B-Instruct-v0.2"] + KEY_ENV_VAR = ( + "TOGETHER_API_KEY_5" # TODO: change this depending on which key to use + ) MAX_TOKENS = 600 diff --git a/src/sample.py b/src/sample.py index af00a6d..0c6b920 100644 --- a/src/sample.py +++ b/src/sample.py @@ -32,12 +32,17 @@ np.random.seed(42) -def sample_categories(feedback: list[Feedback], model_args: ModelArguments, num_categories: int): +def sample_categories( + feedback: list[Feedback], model_args: ModelArguments, num_categories: int +): """Sample categories for feedback and update feedback in-place""" category_model = get_model(model_args) responses = category_model.get_responses( - [[SAMPLE_PROMPT_CATEGORIES.format(count=num_categories, topic=f.domain)] for f in feedback], - SAMPLE_PROMPT_CATEGORIES_CONFIG + [ + [SAMPLE_PROMPT_CATEGORIES.format(count=num_categories, topic=f.domain)] + for f in feedback + ], + SAMPLE_PROMPT_CATEGORIES_CONFIG, ) # We cannot tolerate failed API calls here @@ -45,23 +50,37 @@ def sample_categories(feedback: list[Feedback], model_args: ModelArguments, num_ responses = [r.split("REVISED_CATEGORIES:")[-1].strip() for r in responses] responses = [split_numbered_list(r) for r in responses] - assert all([len(r) == num_categories for r in responses]), "Category generation failed" + assert all([len(r) == num_categories for r in responses]), ( + "Category generation failed" + ) for f, r in zip(feedback, responses): f.categories = r -def sample_prompts(feedback: list[Feedback], model_args: ModelArguments, num_prompts: int, prompts_per_category: int, negative: bool = False): +def sample_prompts( + feedback: list[Feedback], + model_args: ModelArguments, + num_prompts: int, + prompts_per_category: int, + negative: bool = False, +): """Sample prompts for feedback and update feedback in-place""" prompt = SAMPLE_PROMPTS if not negative else SAMPLE_NEGATIVE_PROMPTS - prompt_config = SAMPLE_PROMPTS_CONFIG if not negative else SAMPLE_NEGATIVE_PROMPTS_CONFIG + prompt_config = ( + SAMPLE_PROMPTS_CONFIG if not negative else SAMPLE_NEGATIVE_PROMPTS_CONFIG + ) prompt_model = get_model(model_args) # Get responses for flattened list of prompts responses = prompt_model.get_responses( - [[prompt.format(count=prompts_per_category, domain=f.domain, category=c)] - for f in feedback for c in f.categories], - prompt_config) + [ + [prompt.format(count=prompts_per_category, domain=f.domain, category=c)] + for f in feedback + for c in f.categories + ], + prompt_config, + ) # We cannot tolerate failed API calls here assert all([r is not None for r in responses]), "Prompt generation failed" @@ -79,9 +98,7 @@ def sample_prompts(feedback: list[Feedback], model_args: ModelArguments, num_pro assert all([len(r) == num_prompts for r in responses]), "Prompt generation failed" for f, r in zip(feedback, responses): - dataset = Dataset.from_dict({ - "prompt": r - }) + dataset = Dataset.from_dict({"prompt": r}) if not negative: f.prompts = dataset else: @@ -94,7 +111,11 @@ def add_general_prompts(feedback: list[Feedback], data_dir: str, num_prompts: in f.general_prompts = GeneralPromptDataset.load(data_dir, num_prompts) -def sample_completions(feedback: list[Feedback], model_args: ModelArguments, prompt_type: Literal["prompts", "negative_prompts", "general_prompts"]): +def sample_completions( + feedback: list[Feedback], + model_args: ModelArguments, + prompt_type: Literal["prompts", "negative_prompts", "general_prompts"], +): """Sample completions for feedback and update feedback in-place""" completion_model = get_model(model_args) @@ -106,55 +127,96 @@ def sample_completions(feedback: list[Feedback], model_args: ModelArguments, pro elif prompt_type == "general_prompts": datasets = [f.general_prompts for f in feedback] else: - raise ValueError(f"Invalid prompt type: {prompt_type} (must be one of 'prompts', 'negative_prompts', 'general_prompts')") + raise ValueError( + f"Invalid prompt type: {prompt_type} (must be one of 'prompts', 'negative_prompts', 'general_prompts')" + ) num_prompts = [len(dataset) for dataset in datasets] - all_domains = [f.domain for f, num in zip(feedback, num_prompts) for _ in range(num)] + all_domains = [ + f.domain for f, num in zip(feedback, num_prompts) for _ in range(num) + ] all_effect = [f.effect for f, num in zip(feedback, num_prompts) for _ in range(num)] - all_feedback = [f.content for f, num in zip(feedback, num_prompts) for _ in range(num)] + all_feedback = [ + f.content for f, num in zip(feedback, num_prompts) for _ in range(num) + ] all_prompts = [prompt for dataset in datasets for prompt in dataset["prompt"]] # Get completions for flattened list of prompts # Dict to hold all responses all_responses = {} - all_responses['baseline_response'] = completion_model.get_responses([ - [GET_BASELINE_COMPLETION.format(domain=d, prompt=p)] for d, p in zip(all_domains, all_prompts) - ], GET_BASELINE_COMPLETION_CONFIG) + all_responses["baseline_response"] = completion_model.get_responses( + [ + [GET_BASELINE_COMPLETION.format(domain=d, prompt=p)] + for d, p in zip(all_domains, all_prompts) + ], + GET_BASELINE_COMPLETION_CONFIG, + ) # Get revised completions for flattened list of prompts - all_responses['revised_response'] = completion_model.get_responses([ - [GET_COMPLETION_REVISED.format(prompt=p, feedback=c, response=r)] for p, c, r in zip(all_prompts, all_effect, all_responses['baseline_response']) - ], GET_COMPLETION_REVISED_CONFIG) - all_responses['revised_response'] = [r.split("IMPROVED_RESPONSE:")[-1].strip() if r is not None else r for r in all_responses['revised_response']] + all_responses["revised_response"] = completion_model.get_responses( + [ + [GET_COMPLETION_REVISED.format(prompt=p, feedback=c, response=r)] + for p, c, r in zip( + all_prompts, all_effect, all_responses["baseline_response"] + ) + ], + GET_COMPLETION_REVISED_CONFIG, + ) + all_responses["revised_response"] = [ + r.split("IMPROVED_RESPONSE:")[-1].strip() if r is not None else r + for r in all_responses["revised_response"] + ] # Get responses where feedback is applied in-context - all_responses['in_context_response'] = completion_model.get_responses([ - [GET_IN_CONTEXT_COMPLETION.format(prompt=p, feedback=c)] for p, c in zip(all_prompts, all_feedback) - ], GET_IN_CONTEXT_COMPLETION_CONFIG) + all_responses["in_context_response"] = completion_model.get_responses( + [ + [GET_IN_CONTEXT_COMPLETION.format(prompt=p, feedback=c)] + for p, c in zip(all_prompts, all_feedback) + ], + GET_IN_CONTEXT_COMPLETION_CONFIG, + ) # Get responses where feedback is applied in-context - all_responses['cot_response'] = completion_model.get_responses([ - [GET_COT_COMPLETION.format(prompt=p, feedback=c)] for p, c in zip(all_prompts, all_feedback) - ], GET_COT_COMPLETION_CONFIG) - all_responses['cot_response'] = [r.split("RESPONSE:")[-1].strip() for r in all_responses['cot_response']] - + all_responses["cot_response"] = completion_model.get_responses( + [ + [GET_COT_COMPLETION.format(prompt=p, feedback=c)] + for p, c in zip(all_prompts, all_feedback) + ], + GET_COT_COMPLETION_CONFIG, + ) + all_responses["cot_response"] = [ + r.split("RESPONSE:")[-1].strip() for r in all_responses["cot_response"] + ] + # Split responses into lists of prompts for each feedback for key in all_responses: - all_responses[key] = np.array_split(all_responses[key], np.cumsum(num_prompts)[:-1]) + all_responses[key] = np.array_split( + all_responses[key], np.cumsum(num_prompts)[:-1] + ) for i, f in enumerate(feedback): dataset = datasets[i] num_prompt = num_prompts[i] for key in all_responses: completions = all_responses[key][i] - assert len(completions) == num_prompt, f"Completion generation failed for {key} (got {len(completions)}, expected: {num_prompt})" + assert len(completions) == num_prompt, ( + f"Completion generation failed for {key} (got {len(completions)}, expected: {num_prompt})" + ) dataset = dataset.add_column(key, completions) # Filter out instances where any of the responses is None len_pre_filter = len(dataset) - dataset = dataset.filter(lambda x: x['baseline_response'] is not None and x['revised_response'] is not None and x['in_context_response'] is not None) + dataset = dataset.filter( + lambda x: ( + x["baseline_response"] is not None + and x["revised_response"] is not None + and x["in_context_response"] is not None + ) + ) if len(dataset) < len_pre_filter: - logger.warning(f"Filtered out {len_pre_filter - len(dataset)} instances where the completion API failed") + logger.warning( + f"Filtered out {len_pre_filter - len(dataset)} instances where the completion API failed" + ) if prompt_type == "prompts": f.prompts = dataset @@ -164,36 +226,58 @@ def sample_completions(feedback: list[Feedback], model_args: ModelArguments, pro f.general_prompts = dataset -def sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[Feedback]) -> None: +def sample( + arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[Feedback] +) -> None: model_args, sample_args, _, _ = get_args(arg_dict) run_dir = os.path.join(data_dir, run_id, "sample") logger.info(f"Sampling data for run {run_id}, stored in {run_dir}") # Filter and load feedback - feedback = [f for f in feedback if f.scope in sample_args.scope and f.type in sample_args.type] + feedback = [ + f + for f in feedback + if f.scope in sample_args.scope and f.type in sample_args.type + ] # Skip feedback that was already sampled if we don't want to overwrite if not sample_args.overwrite: feedback = [f for f in feedback if not f.can_load_dataset(run_dir)] - feedback = feedback[:sample_args.num_feedbacks] + feedback = feedback[: sample_args.num_feedbacks] logger.info(f"Loaded {len(feedback)} feedbacks") # Sample categories where necessary - num_categories = np.ceil((max(sample_args.num_prompts, sample_args.num_general_prompts)) / sample_args.prompts_per_category) + num_categories = np.ceil( + (max(sample_args.num_prompts, sample_args.num_general_prompts)) + / sample_args.prompts_per_category + ) sample_categories(feedback, model_args.category_model, num_categories) logger.info("Sampled categories.") - sample_prompts(feedback, model_args.prompt_model, sample_args.num_prompts, sample_args.prompts_per_category) + sample_prompts( + feedback, + model_args.prompt_model, + sample_args.num_prompts, + sample_args.prompts_per_category, + ) logger.info("Sampled prompts.") # Sample prompts outside the feedback domain for non-global feedback - sample_prompts(feedback, model_args.prompt_model, sample_args.num_negative_prompts, sample_args.prompts_per_category, negative=True) + sample_prompts( + feedback, + model_args.prompt_model, + sample_args.num_negative_prompts, + sample_args.prompts_per_category, + negative=True, + ) logger.info("Sampled negative prompts.") # Add prompts from general prompt dataset if feedback[0].general_prompts_available(run_dir) is None: add_general_prompts(feedback, data_dir, sample_args.num_general_prompts) - sample_completions(feedback, model_args.completion_model, prompt_type="general_prompts") + sample_completions( + feedback, model_args.completion_model, prompt_type="general_prompts" + ) logger.info("Sampled general prompt completions.") else: _ = [f.load_cached_general_prompts(run_dir) for f in feedback] @@ -201,17 +285,14 @@ def sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[ # Sample completions sample_completions(feedback, model_args.completion_model, prompt_type="prompts") - sample_completions(feedback, model_args.completion_model, prompt_type="negative_prompts") + sample_completions( + feedback, model_args.completion_model, prompt_type="negative_prompts" + ) logger.info(f"Sampled completions for {len(feedback)} feedbacks") - - - # Save datasets for f in feedback: - f.prompts = f.prompts.train_test_split( - sample_args.train_test_split - ) + f.prompts = f.prompts.train_test_split(sample_args.train_test_split) f.negative_prompts = f.negative_prompts.train_test_split( sample_args.train_test_split ) @@ -225,7 +306,6 @@ def sample(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: list[ logger.info(f"Saved datasets for {len(feedback)} feedbacks") - if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--arg_file", type=str) diff --git a/src/sft_weighted.py b/src/sft_weighted.py index a14641c..f9bb789 100644 --- a/src/sft_weighted.py +++ b/src/sft_weighted.py @@ -9,14 +9,23 @@ class WeightedSFTTrainer(SFTTrainer): - def __init__(self, *args, sigma_soft: float = 0.3, sigma_hard: float = 0.3, response_template: str = "[/INST]", ignore_index: int = -100, **kwargs): + def __init__( + self, + *args, + sigma_soft: float = 0.3, + sigma_hard: float = 0.3, + response_template: str = "[/INST]", + ignore_index: int = -100, + **kwargs, + ): self.response_template = response_template - self.response_token_ids = kwargs["tokenizer"].encode(response_template, add_special_tokens=False) + self.response_token_ids = kwargs["tokenizer"].encode( + response_template, add_special_tokens=False + ) self.ignore_index = ignore_index self.sigma_soft = sigma_soft self.sigma_hard = sigma_hard super().__init__(*args, **kwargs) - def get_completion_only_labels(self, input_ids: list[list[int]]) -> list[list[int]]: labels = torch.tensor(input_ids).clone() @@ -33,19 +42,20 @@ def get_completion_only_labels(self, input_ids: list[list[int]]) -> list[list[in if response_token_ids_start_idx is None: warnings.warn( f"Could not find response key `{self.response_template}` in the " - f'following instance: {self.tokenizer.decode(input_ids)} ' + f"following instance: {self.tokenizer.decode(input_ids)} " f"This instance will be ignored in loss calculation. " f"Note, if this happens often, consider increasing the `max_seq_length`." ) labels[:] = self.ignore_index else: - response_token_ids_end_idx = response_token_ids_start_idx + len(self.response_token_ids) + response_token_ids_end_idx = response_token_ids_start_idx + len( + self.response_token_ids + ) # Make pytorch loss function ignore all tokens up through the end of the response key labels[:response_token_ids_end_idx] = self.ignore_index return labels.tolist() - def compute_loss( self, model: Union[PreTrainedModel, nn.Module], @@ -73,12 +83,16 @@ def compute_loss( hard_outputs = model(**hard_inputs) # Compute combined loss - loss = outputs.loss + self.sigma_soft * soft_outputs.loss + self.sigma_hard * hard_outputs.loss + loss = ( + outputs.loss + + self.sigma_soft * soft_outputs.loss + + self.sigma_hard * hard_outputs.loss + ) if return_outputs: return (loss, outputs) return loss - + def _prepare_non_packed_dataloader( self, tokenizer, @@ -101,13 +115,25 @@ def _prepare_non_packed_dataloader( def tokenize_negatives(row): hard_negative = tokenizer( - row["hard_negative"], truncation=True, padding=False, max_length=max_seq_length, add_special_tokens=False - ) + row["hard_negative"], + truncation=True, + padding=False, + max_length=max_seq_length, + add_special_tokens=False, + ) soft_negative = tokenizer( - row["soft_negative"], truncation=True, padding=False, max_length=max_seq_length, add_special_tokens=False - ) - hard_negative_labels = self.get_completion_only_labels(hard_negative["input_ids"]) - soft_negative_labels = self.get_completion_only_labels(soft_negative["input_ids"]) + row["soft_negative"], + truncation=True, + padding=False, + max_length=max_seq_length, + add_special_tokens=False, + ) + hard_negative_labels = self.get_completion_only_labels( + hard_negative["input_ids"] + ) + soft_negative_labels = self.get_completion_only_labels( + soft_negative["input_ids"] + ) row["hard_negative_input_ids"] = hard_negative["input_ids"] row["hard_negative_attention_mask"] = hard_negative["attention_mask"] row["hard_negative_labels"] = hard_negative_labels @@ -119,4 +145,4 @@ def tokenize_negatives(row): # TODO: remove hacky way of removing string columns from dataset while allowing "extra" features to be passed through dataset = dataset.map(tokenize_negatives, batched=False) dataset = dataset.remove_columns(["hard_negative", "soft_negative", "text"]) - return dataset \ No newline at end of file + return dataset diff --git a/src/train.py b/src/train.py index dbce96b..3c81256 100644 --- a/src/train.py +++ b/src/train.py @@ -16,29 +16,47 @@ from src.sft_weighted import WeightedSFTTrainer from src.dataset.format import to_dpo, to_sft, to_lcdpo, to_sft_weighted from src.feedback import manual_feedback as all_feedback -from src.utils import get_args, find_all_linear_names, PeftSavingCallback, get_train_file_name, print_num_trainable_params, TrainingArguments, find_file_with_prefix - - -def filter_relevant_feedback(feedback: Feedback, prompts: Dataset | None) -> Dataset | None: +from src.utils import ( + get_args, + find_all_linear_names, + PeftSavingCallback, + get_train_file_name, + print_num_trainable_params, + TrainingArguments, + find_file_with_prefix, +) + + +def filter_relevant_feedback( + feedback: Feedback, prompts: Dataset | None +) -> Dataset | None: """Filter out prompts where the revision is not better than the baseline""" if prompts is None: return None - + # TODO: enable this for quantitative feedback # TODO: add support to define "better" using a margin rather than just binary comparison if isinstance(feedback.metric, list): + def metric(x): - return all([f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)]) + return all( + [f(x, v) for f, v in zip(feedback.metric, feedback.metric_value)] + ) else: + def metric(x): return feedback.metric(x, feedback.metric_value) - return prompts.filter(lambda x: feedback.comparison( - metric(x["baseline_response"]), - metric(x["revised_response"]) - )) + return prompts.filter( + lambda x: feedback.comparison( + metric(x["baseline_response"]), metric(x["revised_response"]) + ) + ) -def get_prompts(feedback: Feedback, training_args: TrainingArguments) -> Tuple[Dataset, Dataset, Dataset]: + +def get_prompts( + feedback: Feedback, training_args: TrainingArguments +) -> Tuple[Dataset, Dataset, Dataset]: # Fetch dataset prompts = feedback.prompts["train"].shuffle(seed=42) negative_prompts = feedback.negative_prompts["train"].shuffle(seed=42) @@ -46,7 +64,9 @@ def get_prompts(feedback: Feedback, training_args: TrainingArguments) -> Tuple[D # Filter out prompts where the revision is not better than the baseline if training_args.filter_relevant_feedback: - assert feedback.type == Type.quantitative, "Filtering relevant feedback is currently only supported for quantitative feedback" + assert feedback.type == Type.quantitative, ( + "Filtering relevant feedback is currently only supported for quantitative feedback" + ) prompts = filter_relevant_feedback(feedback, prompts) negative_prompts = filter_relevant_feedback(feedback, negative_prompts) general_prompts = filter_relevant_feedback(feedback, general_prompts) @@ -55,12 +75,20 @@ def get_prompts(feedback: Feedback, training_args: TrainingArguments) -> Tuple[D prompts = prompts.select(range(min(training_args.max_prompts, len(prompts)))) logger.info(f"Using {len(prompts)} prompts") - if training_args.negative_prompt_ratio > 0 and training_args.algo != "lcdpo" and training_args.algo != "sft_weighted": + if ( + training_args.negative_prompt_ratio > 0 + and training_args.algo != "lcdpo" + and training_args.algo != "sft_weighted" + ): num_negative_prompts = int(training_args.negative_prompt_ratio * len(prompts)) negative_prompts = negative_prompts.select(range(num_negative_prompts)) logger.info(f"Using {len(negative_prompts)} negative prompts") - if training_args.general_prompt_ratio > 0 and training_args.algo != "lcdpo" and training_args.algo != "sft_weighted": + if ( + training_args.general_prompt_ratio > 0 + and training_args.algo != "lcdpo" + and training_args.algo != "sft_weighted" + ): num_general_prompts = int(training_args.general_prompt_ratio * len(prompts)) general_prompts = general_prompts.select(range(num_general_prompts)) logger.info(f"Using {len(general_prompts)} general prompts") @@ -68,28 +96,42 @@ def get_prompts(feedback: Feedback, training_args: TrainingArguments) -> Tuple[D return prompts, negative_prompts, general_prompts -def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedback, second_feedback: Feedback = None) -> None: +def train( + arg_dict: dict[str, Any], + run_id: str, + data_dir: str, + feedback: Feedback, + second_feedback: Feedback = None, +) -> None: model_args, _, training_args, _ = get_args(arg_dict) - + # Load feedback run_dir = os.path.join(data_dir, run_id, "sample") logger.info(f"Training using data for run {run_id}, stored in {run_dir}") if not feedback.can_load_dataset(run_dir): - raise ValueError(f"Feedback \"{feedback.content}\" has not been sampled yet") + raise ValueError(f'Feedback "{feedback.content}" has not been sampled yet') feedback.load_dataset(run_dir) - logger.info(f"Loaded feedback \"{feedback.content}\"") + logger.info(f'Loaded feedback "{feedback.content}"') # Load second feedback if given if second_feedback is not None: - assert training_args.multi_feedback_training, "Must set multi_feedback_training to True when providing a second feedback" + assert training_args.multi_feedback_training, ( + "Must set multi_feedback_training to True when providing a second feedback" + ) if not second_feedback.can_load_dataset(run_dir): - raise ValueError(f"Feedback \"{second_feedback.content}\" has not been sampled yet") + raise ValueError( + f'Feedback "{second_feedback.content}" has not been sampled yet' + ) second_feedback.load_dataset(run_dir) elif training_args.multi_feedback_training and second_feedback is None: - raise ValueError("Must provide a second feedback when multi_feedback_training is True") + raise ValueError( + "Must provide a second feedback when multi_feedback_training is True" + ) run_dir = os.path.join(data_dir, run_id, "train") - assert training_args.algo in ["dpo", "sft", "lcdpo", "sft_weighted"], f"Unknown algorithm {training_args.algo}" + assert training_args.algo in ["dpo", "sft", "lcdpo", "sft_weighted"], ( + f"Unknown algorithm {training_args.algo}" + ) train_dir = get_train_file_name(training_args, model_args.train_model) run_dir = os.path.join(run_dir, feedback.file_name) @@ -99,13 +141,17 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba run_dir = os.path.join(run_dir, train_dir) # Load model - assert model_args.train_model.platform == "huggingface", "Only HuggingFace models are supported for training" + assert model_args.train_model.platform == "huggingface", ( + "Only HuggingFace models are supported for training" + ) model = get_model(model_args.train_model) logger.info("Loaded model") prompts, negative_prompts, general_prompts = get_prompts(feedback, training_args) if training_args.multi_feedback_training: - prompts2, negative_prompts2, general_prompts2 = get_prompts(second_feedback, training_args) + prompts2, negative_prompts2, general_prompts2 = get_prompts( + second_feedback, training_args + ) prompts = concatenate_datasets([prompts, prompts2]) negative_prompts = concatenate_datasets([negative_prompts, negative_prompts2]) general_prompts = concatenate_datasets([general_prompts, general_prompts2]) @@ -125,28 +171,46 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba dataset = dataset_constructor( prompts, - negative_prompts if (training_args.negative_prompt_ratio > 0 or training_args.algo == "lcdpo" or training_args.algo == "sft_weighted") else None, - general_prompts if (training_args.negative_prompt_ratio > 0 or training_args.algo == "lcdpo" or training_args.algo == "sft_weighted") else None, - model_args.train_model.model_name_or_path) - + negative_prompts + if ( + training_args.negative_prompt_ratio > 0 + or training_args.algo == "lcdpo" + or training_args.algo == "sft_weighted" + ) + else None, + general_prompts + if ( + training_args.negative_prompt_ratio > 0 + or training_args.algo == "lcdpo" + or training_args.algo == "sft_weighted" + ) + else None, + model_args.train_model.model_name_or_path, + ) - # Load base training arg adapter if given + # Load base training arg adapter if given if training_args.use_base_prefix is not None: base_run_dir = os.path.join(data_dir, run_id, "train", feedback.file_name) - adapter_name = find_file_with_prefix(base_run_dir, training_args.use_base_prefix) - model.model = PeftModel.from_pretrained(model.model, os.path.join(base_run_dir, adapter_name), is_trainable=True) + adapter_name = find_file_with_prefix( + base_run_dir, training_args.use_base_prefix + ) + model.model = PeftModel.from_pretrained( + model.model, os.path.join(base_run_dir, adapter_name), is_trainable=True + ) logger.info(f"Loaded base training model from {base_run_dir}") - + # Add LoRA config assert training_args.lora_enable, "Currently only LoRA training is supported" if training_args.lora_enable and training_args.use_base_prefix is None: peft_config = LoraConfig( - r=training_args.lora_r, - lora_alpha=training_args.lora_alpha, - target_modules = find_all_linear_names(model.model, training_args.lora_exclude), - lora_dropout=training_args.lora_dropout, + r=training_args.lora_r, + lora_alpha=training_args.lora_alpha, + target_modules=find_all_linear_names( + model.model, training_args.lora_exclude + ), + lora_dropout=training_args.lora_dropout, bias=training_args.lora_bias, - task_type="CAUSAL_LM" + task_type="CAUSAL_LM", ) else: peft_config = None @@ -162,22 +226,33 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba model.model.config.use_cache = False # Create eval dataset - dataset = dataset.train_test_split(test_size=training_args.eval_split, seed=42, shuffle=True) + dataset = dataset.train_test_split( + test_size=training_args.eval_split, seed=42, shuffle=True + ) eval_dataset = dataset["test"] dataset = dataset["train"] - logger.info(f"Training on {len(dataset)} prompts, evaluating on {len(eval_dataset)} prompts for feedback \"{feedback.content}\"") + logger.info( + f'Training on {len(dataset)} prompts, evaluating on {len(eval_dataset)} prompts for feedback "{feedback.content}"' + ) # TODO: hacky, remove - if model_args.train_model.model_name_or_path in ["tiiuae/falcon-7b-instruct", "01-ai/Yi-6B-Chat", "Qwen/Qwen-7B-Chat"]: + if model_args.train_model.model_name_or_path in [ + "tiiuae/falcon-7b-instruct", + "01-ai/Yi-6B-Chat", + "Qwen/Qwen-7B-Chat", + ]: response_template = "<|im_start|>assistant\n" - elif model_args.train_model.model_name_or_path == "NousResearch/Nous-Hermes-llama-2-7b": + elif ( + model_args.train_model.model_name_or_path + == "NousResearch/Nous-Hermes-llama-2-7b" + ): response_template = "Response:\n" else: response_template = "[/INST]" if training_args.algo == "dpo": - model.tokenizer.padding_side = 'left' + model.tokenizer.padding_side = "left" trainer = DPOTrainer( model=model.model, max_length=2048, @@ -188,10 +263,10 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba eval_dataset=eval_dataset, tokenizer=model.tokenizer, peft_config=peft_config, - callbacks=[PeftSavingCallback] if training_args.lora_enable else None + callbacks=[PeftSavingCallback] if training_args.lora_enable else None, ) elif training_args.algo == "lcdpo": - model.tokenizer.padding_side = 'left' + model.tokenizer.padding_side = "left" trainer = LocallyConstrainedDPOTrainer( model=model.model, max_length=2048, @@ -209,11 +284,13 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba tokenizer=model.tokenizer, response_template=response_template, peft_config=peft_config, - callbacks=[PeftSavingCallback] if training_args.lora_enable else None + callbacks=[PeftSavingCallback] if training_args.lora_enable else None, ) elif training_args.algo == "sft": - model.tokenizer.padding_side = 'right' - collator = DataCollatorForCompletionOnlyLM(response_template, tokenizer=model.tokenizer) + model.tokenizer.padding_side = "right" + collator = DataCollatorForCompletionOnlyLM( + response_template, tokenizer=model.tokenizer + ) trainer = SFTTrainer( model=model.model, args=training_args, @@ -223,12 +300,14 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba data_collator=collator, max_seq_length=2048, peft_config=peft_config, - callbacks=[PeftSavingCallback] if training_args.lora_enable else None + callbacks=[PeftSavingCallback] if training_args.lora_enable else None, ) elif training_args.algo == "sft_weighted": - model.tokenizer.padding_side = 'right' - collator = DataCollatorForCompletionOnlyLM(response_template, tokenizer=model.tokenizer) - training_args.evaluation_strategy = "no" # TODO: getting error during eval + model.tokenizer.padding_side = "right" + collator = DataCollatorForCompletionOnlyLM( + response_template, tokenizer=model.tokenizer + ) + training_args.evaluation_strategy = "no" # TODO: getting error during eval trainer = WeightedSFTTrainer( model=model.model, args=training_args, @@ -241,7 +320,7 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba sigma_soft=training_args.lcdpo_sigma_soft, sigma_hard=training_args.lcdpo_sigma_hard, peft_config=peft_config, - callbacks=[PeftSavingCallback] if training_args.lora_enable else None + callbacks=[PeftSavingCallback] if training_args.lora_enable else None, ) else: raise ValueError(f"Unknown algorithm {training_args.algo}") @@ -253,7 +332,7 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba if training_args.report_to == "wandb": wandb.finish() - + # Sometimes Wandb needs more time to close resources sleep(2) @@ -272,6 +351,6 @@ def train(arg_dict: dict[str, Any], run_id: str, data_dir: str, feedback: Feedba feedback = all_feedback if args.feedback_prefix is not None: feedback = [f for f in feedback if f.content.startswith(args.feedback_prefix)] - + for f in feedback: train(arg_dict, args.run_id, args.data_dir, f) diff --git a/src/utils.py b/src/utils.py index a3d114a..550d5bf 100644 --- a/src/utils.py +++ b/src/utils.py @@ -10,7 +10,11 @@ from dataclasses import field, dataclass, asdict import torch -from transformers import TrainingArguments as TransformerTrainingArguments, TrainerCallback, HfArgumentParser +from transformers import ( + TrainingArguments as TransformerTrainingArguments, + TrainerCallback, + HfArgumentParser, +) from src.logger import logger from src.dataset.feedback_utils import Scope, Type @@ -19,11 +23,7 @@ DEVICE = "cuda" if torch.cuda.is_available() else "cpu" -DTYPES = { - "bf16": torch.bfloat16, - "f16": torch.float16, - "f32": torch.float32 -} +DTYPES = {"bf16": torch.bfloat16, "f16": torch.float16, "f32": torch.float32} @dataclass @@ -37,13 +37,15 @@ class ModelArguments: @dataclass class PipelineModelsArguments: - category_model: Optional[ModelArguments] = field(default_factory=ModelArguments) - prompt_model: Optional[ModelArguments] = field(default_factory=ModelArguments) - completion_model: Optional[ModelArguments] = field(default_factory=ModelArguments) - train_model: Optional[ModelArguments] = field(default_factory=ModelArguments) - qualitative_eval_model: Optional[ModelArguments] = field(default_factory=ModelArguments) - - def __post_init__(self): + category_model: Optional[ModelArguments] = field(default_factory=ModelArguments) + prompt_model: Optional[ModelArguments] = field(default_factory=ModelArguments) + completion_model: Optional[ModelArguments] = field(default_factory=ModelArguments) + train_model: Optional[ModelArguments] = field(default_factory=ModelArguments) + qualitative_eval_model: Optional[ModelArguments] = field( + default_factory=ModelArguments + ) + + def __post_init__(self): # TODO: figure out a way to not parse separately and preserve types self.category_model = ModelArguments(**self.category_model) self.prompt_model = ModelArguments(**self.prompt_model) @@ -54,9 +56,13 @@ def __post_init__(self): @dataclass class SampleArguments: - scope: Optional[list[Scope]] = field(default_factory= lambda: ["global_", "regional", "local"]) - type: Optional[list[Type]] = field(default_factory=lambda: ["quantitative", "qualitative"]) - train_test_split: float = 0.2 # percentage of data used for test set + scope: Optional[list[Scope]] = field( + default_factory=lambda: ["global_", "regional", "local"] + ) + type: Optional[list[Type]] = field( + default_factory=lambda: ["quantitative", "qualitative"] + ) + train_test_split: float = 0.2 # percentage of data used for test set num_feedbacks: Optional[int] = 1 prompts_per_category: Optional[int] = 16 num_prompts: Optional[int] = 32 @@ -108,14 +114,20 @@ class EvalArguments: class PeftSavingCallback(TrainerCallback): def on_save(self, args, state, control, **kwargs): - checkpoint_path = os.path.join(args.output_dir, f"checkpoint-{state.global_step}") + checkpoint_path = os.path.join( + args.output_dir, f"checkpoint-{state.global_step}" + ) kwargs["model"].save_pretrained(checkpoint_path) if "pytorch_model.bin" in os.listdir(checkpoint_path): os.remove(os.path.join(checkpoint_path, "pytorch_model.bin")) -def dump_arg_dicts(arg_dicts: dict[str, dict[str, Any]], output_dir: str, filename: str = "arg_dict.json"): +def dump_arg_dicts( + arg_dicts: dict[str, dict[str, Any]], + output_dir: str, + filename: str = "arg_dict.json", +): arg_dict = {name: asdict(arg) for name, arg in arg_dicts.items()} # Create output dir if it doesn't exist @@ -126,7 +138,9 @@ def dump_arg_dicts(arg_dicts: dict[str, dict[str, Any]], output_dir: str, filena json.dump(arg_dict, f, indent=2) -def get_args(arg_dict: dict[str, Any]) -> tuple[PipelineModelsArguments, SampleArguments, TrainingArguments, EvalArguments]: +def get_args( + arg_dict: dict[str, Any], +) -> tuple[PipelineModelsArguments, SampleArguments, TrainingArguments, EvalArguments]: modal_arg_dict = arg_dict["model_args"] sample_arg_dict = arg_dict["sample_args"] training_arg_dict = arg_dict["training_args"] @@ -138,7 +152,9 @@ def get_args(arg_dict: dict[str, Any]) -> tuple[PipelineModelsArguments, SampleA sample_arg_parser = HfArgumentParser(SampleArguments) sample_args: SampleArguments = sample_arg_parser.parse_dict(sample_arg_dict)[0] training_arg_parser = HfArgumentParser(TrainingArguments) - training_args: TrainingArguments = training_arg_parser.parse_dict(training_arg_dict)[0] + training_args: TrainingArguments = training_arg_parser.parse_dict( + training_arg_dict + )[0] eval_arg_parser = HfArgumentParser(EvalArguments) eval_args: EvalArguments = eval_arg_parser.parse_dict(eval_arg_dict)[0] return model_args, sample_args, training_args, eval_args @@ -152,27 +168,32 @@ def find_all_linear_names(model, exclude: list[str]): if any(keyword in name for keyword in exclude): continue if isinstance(module, cls): - names = name.split('.') + names = name.split(".") lora_module_names.add(names[-1]) return list(lora_module_names) def format_messages(batch: list[list[str]]) -> list[list[dict[str, str]]]: """Assumes batch is a list of lists of strings, where each inner list is a list of chat messages that alternate between user and assistant.""" - return [[{ - "role": "user" if i % 2 == 0 else "assistant", - "content": m - } for i, m in enumerate(b)] for b in batch] + return [ + [ + {"role": "user" if i % 2 == 0 else "assistant", "content": m} + for i, m in enumerate(b) + ] + for b in batch + ] def split_numbered_list(text: str) -> list[str]: """Splits a numbered list into a list of strings, where each string is a numbered item in the list.""" - text = '\n' + text + text = "\n" + text regex = r"\n[0-9]+. " return [t.strip() for t in re.split(regex, text) if t.strip()] -def throttle(lock: threading.Lock, rqi: int, last_requests: list[float], interval: int = 60) -> None: +def throttle( + lock: threading.Lock, rqi: int, last_requests: list[float], interval: int = 60 +) -> None: """Decorator to throttle the number of requests per interval. Args: @@ -181,6 +202,7 @@ def throttle(lock: threading.Lock, rqi: int, last_requests: list[float], interva last_requests: List to store timestamps of the last requests interval: Interval in seconds to check the number of requests """ + def decorator(func): @wraps(func) def wrapper(*args, **kwargs): @@ -189,7 +211,9 @@ def wrapper(*args, **kwargs): with lock: # Remove timestamps older than 60 seconds now = time.time() - last_requests = [req for req in last_requests if now - req < interval] + last_requests = [ + req for req in last_requests if now - req < interval + ] # If the number of requests in the last minute is less than the limit, send a new request if len(last_requests) < rqi: last_requests.append(now) @@ -198,12 +222,15 @@ def wrapper(*args, **kwargs): minimum = min([now - req for req in last_requests]) time.sleep(minimum) return func(*args, **kwargs) + return wrapper + return decorator def catch_error_return_none(func): """Decorator to log a warning if an error occurs but catches the error and returns None.""" + @wraps(func) def wrapper(*args, **kwargs): try: @@ -211,6 +238,7 @@ def wrapper(*args, **kwargs): except Exception as e: logger.warning(f"An error occurred in {func.__name__}: {e}") return None + return wrapper @@ -227,12 +255,15 @@ def wrapper(*args, **kwargs): "max_prompts": None, "multi_feedback_training": False, "model_name_or_path": "mistralai/Mistral-7B-Instruct-v0.2", - "use_base_prefix": None + "use_base_prefix": None, } -def get_train_file_name(training_args: TrainingArguments, model_args: ModelArguments) -> str: + +def get_train_file_name( + training_args: TrainingArguments, model_args: ModelArguments +) -> str: file_name_parts = [training_args.algo] - + def append_if_different(arg_value, key): if arg_value != defaults[key]: arg_value = str(arg_value).replace("/", "-") @@ -246,17 +277,17 @@ def append_if_different(arg_value, key): else: append_if_different(training_args.negative_prompt_ratio, "hard") append_if_different(training_args.general_prompt_ratio, "soft") - + append_if_different(training_args.learning_rate, "lr") - + if training_args.lora_enable: append_if_different(training_args.lora_r, "r") append_if_different(training_args.lora_alpha, "alpha") - + append_if_different(training_args.num_train_epochs, "epochs") append_if_different(model_args.model_name_or_path, "model_name_or_path") append_if_different(training_args.use_base_prefix, "use_base_prefix") - + if training_args.multi_feedback_training: file_name_parts.append("-multi") @@ -265,23 +296,24 @@ def append_if_different(arg_value, key): if training_args.lcdpo_avg_kl: file_name_parts.append("-avgkl") - + append_if_different(training_args.max_prompts, "max_prompts") - + # Serialize training_args and generate a short hash to ensure any parameter changes are reflected in the file name args_copy = copy.deepcopy(training_args) del args_copy.logging_dir short_hash = hashlib.sha1(args_copy.to_json_string().encode()).hexdigest()[:8] file_name_parts.append(f"-{short_hash}") - - return ''.join(file_name_parts) + + return "".join(file_name_parts) def print_num_trainable_params(model): - trainable_params = sum(p.numel() - for p in model.parameters() if p.requires_grad) + trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) all_params = sum(p.numel() for p in model.parameters()) - logger.info(f"Trainable params: {trainable_params}/{all_params} ({trainable_params/all_params:.2%})") + logger.info( + f"Trainable params: {trainable_params}/{all_params} ({trainable_params / all_params:.2%})" + ) def find_file_with_prefix(path: str, prefix: str) -> str: @@ -289,4 +321,4 @@ def find_file_with_prefix(path: str, prefix: str) -> str: for file in os.listdir(path): if file.startswith(prefix): return file - raise FileNotFoundError(f"No file with prefix {prefix} found in {path}") \ No newline at end of file + raise FileNotFoundError(f"No file with prefix {prefix} found in {path}") diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 6c7fc71..4027a7b 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -8,61 +8,75 @@ def test_length(): # Test with non-English characters assert Metric.length("こんにちは世界", None) == 7 + def test_contains_any_string(): assert Metric.contains_any_string("Hello, World!", ["Hello", "Python"]) assert not Metric.contains_any_string("Hello, World!", ["Python", "Java"]) # Case sensitivity test assert Metric.contains_any_string("hello, world!", ["Hello"]) + def test_contains_all_strings(): assert Metric.contains_all_strings("Hello, World!", ["Hello", "World"]) assert not Metric.contains_all_strings("Hello, World!", ["Hello", "Python"]) # Case sensitivity and partial match test assert Metric.contains_all_strings("Hello, World!", ["hello", "world"]) + def test_contains_none_strings(): assert Metric.contains_none_strings("Hello, World!", ["Python", "Java"]) assert not Metric.contains_none_strings("Hello, World!", ["Hello", "Python"]) # Case sensitivity test assert not Metric.contains_none_strings("Hello, World!", ["hello"]) + def test_contains_phone_number(): assert Metric.contains_phone_number("Call me at (123) 456-7890", "(123) 456-7890") assert Metric.contains_phone_number("Call me at 123-456-7890", "(123) 456-7890") - assert not Metric.contains_phone_number("Call me at (123) 456-7890", "(321) 654-0987") + assert not Metric.contains_phone_number( + "Call me at (123) 456-7890", "(321) 654-0987" + ) + def test_ends_with(): assert Metric.ends_with("Hello, World!", "World!") assert Metric.ends_with("Hello, World!", "world!") # Case insensitivity assert not Metric.ends_with("Hello, World!", "Hello") + def test_ends_with_cleaned(): assert Metric.ends_with_cleaned("Hello, World!!", "world!!") assert not Metric.ends_with_cleaned("Hello, World!!!", "hello!") assert Metric.ends_with_cleaned("Hello,\n World?", "Hello, world?") + def test_regex_search(): assert Metric.regex_search("Hello, World!", r"\bWorld\b") assert not Metric.regex_search("Hello, World!", r"\bPython\b") + def test_regex_search_false(): assert not Metric.regex_search_false("Hello, World!", r"\bWorld\b") assert Metric.regex_search_false("Hello, World!", r"\bPython\b") + def test_is_language(): assert Metric.is_language("Hello, World!", "en") assert not Metric.is_language("Bonjour, Monde!", "en") assert Metric.is_language("Bonjour, Monde!", "fr") + def test_starts_with(): assert Metric.starts_with("Hello, World!", "Hello") assert Metric.starts_with("Hello, World!", "hello") # Case insensitivity assert not Metric.starts_with("Hello, World!", "World") + def test_doesnt_start_with(): assert Metric.doesnt_start_with("Hello, World!", "World") assert not Metric.doesnt_start_with("Hello, World!", "hello") # Case insensitivity + # Additional tests for other metrics if they exist if __name__ == "__main__":