diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7590fecd..10420a276 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9'] + python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v2 @@ -46,7 +46,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.9'] + python-version: ['3.8', '3.9', '3.10', '3.11'] + dependencies: ['dev', 'full'] steps: - uses: actions/checkout@v2 @@ -63,15 +64,16 @@ jobs: - name: Install Dependencies run: | python -m pip install --upgrade pip - make dev + make ${{ matrix.dependencies }} - name: Run Pytests run: | make test-cov - name: Upload to codecov.io - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 with: + token: ${{ secrets.CODECOV_TOKEN }} file: ./coverage.xml flags: unittests name: codecov-umbrella diff --git a/.github/workflows/examples_check.yml b/.github/workflows/examples_check.yml new file mode 100644 index 000000000..f01651382 --- /dev/null +++ b/.github/workflows/examples_check.yml @@ -0,0 +1,34 @@ +name: Notebook Execution and Error Check + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: # This enables manual triggering + +jobs: + execute_notebooks: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + + - name: Install dependencies + run: | + pip install jupyter nbconvert + + - name: Execute notebooks and check for errors + run: | + cd docs/examples + for notebook in $(ls *.ipynb); do + jupyter nbconvert --to notebook --execute "$notebook" + if [ $? -ne 0 ]; then + echo "Error found in $notebook" + exit 1 + fi + done diff --git a/.gitignore b/.gitignore index bf1c70f3b..4996b0624 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,13 @@ openai_api_key.txt *__pycache__* data/* .vscode/* +.venv/* .DS_Store *.ipynb_checkpoints/* *.pyc *.env *.log +*.venv settings.json site/* guardrails.log @@ -16,4 +18,8 @@ dist/* *.rail_output* .idea/* .cache -scratch/ \ No newline at end of file +scratch/ +.coverage* +test.db +test.index +htmlcov diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d9866e645..31d9eea41 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,3 +35,16 @@ For convenience, consider [installing the pre-commit hooks](https://pre-commit.c 5. Celebrate when your pull request is merged! Your changes will be available in the next Guardrails release. Thank you for your contribution and happy coding! + +## Documentation + +Docs are served via mkdocs using the mkdocstring plugin. To serve docs locally, run the following + +```bash +# install dependencies +pip install -e ".[dev]"; + +# serve docs +mkdocs serve; +``` +then navigate to `localhost:8000` in your browser. \ No newline at end of file diff --git a/Makefile b/Makefile index 58fc25dd5..9a4dffc5b 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,10 @@ test-basic: python -c "import guardrails.version as mversion" test-cov: - pytest tests/ --cov=./ --cov-report=xml + pytest tests/ --cov=./guardrails/ --cov-report=xml + +view-test-cov: + pytest tests/ --cov=./guardrails/ --cov-report html && open htmlcov/index.html docs-serve: mkdocs serve -a $(MKDOCS_SERVE_ADDR) @@ -30,4 +33,7 @@ docs-deploy: dev: pip install -e ".[dev]" +full: + pip install -e ".[all]" + all: autoformat lint docs test \ No newline at end of file diff --git a/README.md b/README.md index 3748876fc..971010ccb 100644 --- a/README.md +++ b/README.md @@ -105,11 +105,11 @@ Using `RAIL`, we: Explain what a bank run is in a tweet. -@xml_prefix_prompt +${gr.xml_prefix_prompt} -{output_schema} +${output_schema} -@json_suffix_prompt_v2_wo_none +${gr.json_suffix_prompt_v2_wo_none} ``` diff --git a/docs/0-2-migration.ipynb b/docs/0-2-migration.ipynb new file mode 100644 index 000000000..5f120a5cc --- /dev/null +++ b/docs/0-2-migration.ipynb @@ -0,0 +1,592 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Migrating to 0.2.0\n", + "\n", + "The 0.2.0 release contains a handful of breaking changes compared to the previous 0.1.x releases. This guide will list out these changes as well as how to migrate to the new features that encompass them in 0.2.x." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pydantic Support\n", + "\n", + "In 0.1.x, Guardrails supported pydantic models with a `register_pydantic` decorator. This decorator has been removed in 0.2.x and replaced with the `Guard.from_pydantic` method. This method takes in a pydantic model and returns a `Guard` instance that can be used to validate the model.\n", + "\n", + "To migrate from the `register_pydantic` decorator to the `Guard.from_pydantic` method, instantiate the model outside of the decorator and pass it into the `Guard.from_pydantic` method. If you wish to continue using validators defined on the model with Guardrails, you must define them separately and register them with the `register_validator` decorator.\n", + "\n", + "For example, see the following example migration from a 0.1.x-style rail spec defines a model with the `register_pydantic` decorator to a 0.2.x-style Guard constructed from a pydantic model:\n", + "\n", + "\n", + "=== \"0.1.x\"\n", + " ```xml\n", + "\n", + " \n", + " \n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " Generate data for possible users in accordance with the specification below.\n", + " @xml_prefix_prompt\n", + " {output_schema}\n", + " @complete_json_suffix_v2\n", + " \n", + " \n", + " ```\n", + "\n", + "=== \"0.2.x\"\n", + " ```py\n", + " from guardrails import Guard\n", + " from typing import Any, Dict, List\n", + "\n", + " from pydantic import BaseModel, Field\n", + "\n", + " from guardrails.validators import (\n", + " FailResult,\n", + " PassResult,\n", + " ValidationResult,\n", + " Validator,\n", + " register_validator,\n", + " )\n", + "\n", + "\n", + " @register_validator(name=\"zip_code_must_be_numeric\", data_type=\"string\")\n", + " class ZipCodeMustBeNumeric(Validator):\n", + " def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult:\n", + " if not value.isnumeric():\n", + " return FailResult(error_message=\"Zip code must be numeric.\")\n", + " return PassResult()\n", + "\n", + "\n", + " @register_validator(name=\"age_must_be_between_0_and_150\", data_type=\"integer\")\n", + " class AgeMustBeBetween0And150(Validator):\n", + " def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult:\n", + " if not 0 <= value <= 150:\n", + " return FailResult(error_message=\"Age must be between 0 and 150.\")\n", + " return PassResult()\n", + "\n", + "\n", + " @register_validator(name=\"zip_code_in_california\", data_type=\"string\")\n", + " class ZipCodeInCalifornia(Validator):\n", + " def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult:\n", + " if not value.startswith(\"9\"):\n", + " return FailResult(\n", + " error_message=\"Zip code must be in California, and start with 9.\"\n", + " )\n", + " if value == \"90210\":\n", + " return FailResult(error_message=\"Zip code must not be Beverly Hills.\")\n", + " return PassResult()\n", + "\n", + "\n", + " class Person(BaseModel):\n", + " \"\"\"Information about a person.\n", + " Args:\n", + " name (str): The name of the person.\n", + " age (int): The age of the person.\n", + " zip_code (str): The zip code of the person.\n", + " \"\"\"\n", + "\n", + " name: str\n", + " age: int = Field(..., validators=[AgeMustBeBetween0And150(on_fail=\"reask\")])\n", + " zip_code: str = Field(\n", + " ...,\n", + " validators=[\n", + " ZipCodeMustBeNumeric(on_fail=\"reask\"),\n", + " ZipCodeInCalifornia(on_fail=\"reask\"),\n", + " ],\n", + " )\n", + "\n", + "\n", + " class ListOfPeople(BaseModel):\n", + " \"\"\"A list of people.\n", + " Args:\n", + " people (List[Person]): A list of people.\n", + " \"\"\"\n", + "\n", + " people: List[Person]\n", + "\n", + "\n", + " prompt = \"\"\"\n", + " Generate data for possible users in accordance with the specification below.\n", + " ${gr.xml_prefix_prompt}\n", + " ${output_schema}\n", + " ${gr.complete_json_suffix_v2}\n", + " \"\"\"\n", + "\n", + " guard = Guard.from_pydantic(ListOfPeople, prompt=prompt)\n", + " ```\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Choice\n", + "\n", + "In 0.1.x, the `choice` tag was defined in the following way, and its output placed a choice discriminator on the top level, with another element whose key is the value of the choice, containing the case output:\n", + "\n", + "\n", + "=== \"0.1.x spec\"\n", + " ```xml\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " ```\n", + "=== \"0.1.x output\"\n", + " ```json\n", + " {\n", + " \"action\": \"fight\",\n", + " \"fight\": {\n", + " \"fight_move\": \"punch\"\n", + " }\n", + " }\n", + " ```\n", + "\n", + "\n", + "In 0.2.x, the `choice` tag follows the OpenAPI discriminated union pattern, wherein its output is a single object with a discriminator field nested inside the generated object:\n", + "\n", + "=== \"0.2.x rail spec\"\n", + " ```xml\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " ```\n", + "=== \"0.2.x pydantic guard\"\n", + " ```py\n", + " from typing import Literal, Union\n", + " from pydantic import BaseModel, Field\n", + " from guardrails import Guard\n", + "\n", + "\n", + " class Fight(BaseModel):\n", + " action: Literal[\"fight\"]\n", + " fight_move: str\n", + "\n", + "\n", + " class Flight(BaseModel):\n", + " action: Literal[\"flight\"]\n", + " flight_direction: str\n", + " flight_speed: int\n", + "\n", + "\n", + " class Choice(BaseModel):\n", + " choice: Union[Fight, Flight] = Field(..., discriminator=\"action\")\n", + "\n", + "\n", + " guard = Guard.from_pydantic(Choice)\n", + " ```\n", + "=== \"0.2.x output\"\n", + " ```json\n", + " {\n", + " \"choice\": {\n", + " \"action\": \"fight\",\n", + " \"fight_move\": \"punch\"\n", + " }\n", + " }\n", + " ```\n", + " \n", + "\n", + "Notice that the `choice` tag now has a `discriminator` attribute, which is the name of the field that will be used to determine which case is used. This field is required, and must be a string. Also, the inside of the `case` tag is implicitly wrapped in an object, so the `flight` case is no longer wrapped in an `object` tag." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Changes To Validators\n", + "Previously, Validators had a `validate` method that accepted the key, value, and entire schema to validate a specific property on that schema. It was also expected to return the full schema after performing validation. In 0.2.x this has been simplified. Now `Validator.validate` need only accept the value for the property being validated and any metdata necessary for the validation to run. Also rather than returning the schema, `validate` methods should return a `ValidationResult`. See the below example to see these differences in practice.\n", + "\n", + "=== \"0.1.x\"\n", + " ```py\n", + " @register_validator(name=\"length\", data_type=[\"string\", \"list\"])\n", + " class ValidLength(Validator):\n", + " \"\"\"Validate that the length of value is within the expected range.\n", + "\n", + " - Name for `format` attribute: `length`\n", + " - Supported data types: `string`, `list`, `object`\n", + " - Programmatic fix: If shorter than the minimum, pad with empty last elements.\n", + " If longer than the maximum, truncate.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self, min: int = None, max: int = None, on_fail: Optional[Callable] = None\n", + " ):\n", + " super().__init__(on_fail=on_fail, min=min, max=max)\n", + " self._min = int(min) if min is not None else None\n", + " self._max = int(max) if max is not None else None\n", + "\n", + " def validate(self, key: str, value: Any, schema: Union[Dict, List]) -> Dict:\n", + " \"\"\"Validate that a value is within a range.\"\"\"\n", + " logger.debug(\n", + " f\"Validating {value} is in length range {self._min} - {self._max}...\"\n", + " )\n", + "\n", + " if self._min is not None and len(value) < self._min:\n", + " logger.debug(f\"Value {value} is less than {self._min}.\")\n", + "\n", + " # Repeat the last character to make the value the correct length.\n", + " if isinstance(value, str):\n", + " last_val = value[-1]\n", + " else:\n", + " last_val = [value[-1]]\n", + "\n", + " corrected_value = value + last_val * (self._min - len(value))\n", + " raise EventDetail(\n", + " key,\n", + " value,\n", + " schema,\n", + " f\"Value has length less than {self._min}. \"\n", + " f\"Please return a longer output, \"\n", + " f\"that is shorter than {self._max} characters.\",\n", + " corrected_value,\n", + " )\n", + "\n", + " if self._max is not None and len(value) > self._max:\n", + " logger.debug(f\"Value {value} is greater than {self._max}.\")\n", + " raise EventDetail(\n", + " key,\n", + " value,\n", + " schema,\n", + " f\"Value has length greater than {self._max}. \"\n", + " f\"Please return a shorter output, \"\n", + " f\"that is shorter than {self._max} characters.\",\n", + " value[: self._max],\n", + " )\n", + "\n", + " return schema\n", + " ```\n", + "\n", + "=== \"0.2.x\"\n", + " ```py\n", + " @register_validator(name=\"length\", data_type=[\"string\", \"list\"])\n", + " class ValidLength(Validator):\n", + " \"\"\"Validates that the length of value is within the expected range.\n", + "\n", + " **Key Properties**\n", + "\n", + " | Property | Description |\n", + " | ----------------------------- | --------------------------------- |\n", + " | Name for `format` attribute | `length` |\n", + " | Supported data types | `string`, `list`, `object` |\n", + " | Programmatic fix | If shorter than the minimum, pad with empty last elements. If longer than the maximum, truncate. |\n", + "\n", + " Parameters: Arguments\n", + " min: The inclusive minimum length.\n", + " max: The inclusive maximum length.\n", + " \"\"\"\n", + "\n", + " def __init__(\n", + " self, min: int = None, max: int = None, on_fail: Optional[Callable] = None\n", + " ):\n", + " super().__init__(on_fail=on_fail, min=min, max=max)\n", + " self._min = int(min) if min is not None else None\n", + " self._max = int(max) if max is not None else None\n", + "\n", + " def validate(self, value: Any, metadata: Dict) -> ValidationResult:\n", + " \"\"\"Validates that the length of value is within the expected range.\"\"\"\n", + " logger.debug(\n", + " f\"Validating {value} is in length range {self._min} - {self._max}...\"\n", + " )\n", + "\n", + " if self._min is not None and len(value) < self._min:\n", + " logger.debug(f\"Value {value} is less than {self._min}.\")\n", + "\n", + " # Repeat the last character to make the value the correct length.\n", + " if isinstance(value, str):\n", + " last_val = value[-1]\n", + " else:\n", + " last_val = [value[-1]]\n", + "\n", + " corrected_value = value + last_val * (self._min - len(value))\n", + " return FailResult(\n", + " error_message=f\"Value has length less than {self._min}. \"\n", + " f\"Please return a longer output, \"\n", + " f\"that is shorter than {self._max} characters.\",\n", + " fix_value=corrected_value,\n", + " )\n", + "\n", + " if self._max is not None and len(value) > self._max:\n", + " logger.debug(f\"Value {value} is greater than {self._max}.\")\n", + " return FailResult(\n", + " error_message=f\"Value has length greater than {self._max}. \"\n", + " f\"Please return a shorter output, \"\n", + " f\"that is shorter than {self._max} characters.\",\n", + " fix_value=value[: self._max],\n", + " )\n", + "\n", + " return PassResult()\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Removal Of Script Support\n", + "\n", + "With additional first class support for Pydantic models as described above, support for custom code via the `\n", + "\n", + " \n", + " \"\"\"\n", + "\n", + " guard = Guard.from_rail_string(rail_string=rail_str)\n", + " ```\n", + "=== \"0.2.x\"\n", + " ```py\n", + " from guardrails import Guard\n", + "\n", + " script_var = \"I'm the script variable!\"\n", + "\n", + " rail_str = \"\"\"\n", + " \n", + "\n", + " \n", + "\n", + " \n", + " \"\"\".format(script_var=script_var)\n", + "\n", + " guard = Guard.from_rail_string(rail_string=rail_str)\n", + " ```\n", + "\n", + "\n", + "Or you can use prompt parameters to pass these values in when executing the Guard via `__call__` or `parse`:\n", + "\n", + "\n", + "=== \"0.1.x\"\n", + " ```py\n", + " from guardrails import Guard\n", + "\n", + " rail_str = \"\"\"\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "\n", + " \n", + " \"\"\"\n", + "\n", + " guard = Guard.from_rail_string(rail_string=rail_str)\n", + " ```\n", + "=== \"0.2.x\"\n", + " ```py\n", + " from guardrails import Guard\n", + "\n", + " script_var = \"I'm the script variable!\"\n", + "\n", + " rail_str = \"\"\"\n", + " \n", + "\n", + " \n", + "\n", + " \n", + " \"\"\"\n", + "\n", + " guard = Guard.from_rail_string(rail_string=rail_str)\n", + "\n", + " guard(\n", + " ...,\n", + " prompt_params={ \"script_var\": script_var }\n", + " )\n", + " ```\n", + "\n", + "\n", + "Alternatively if you were using scripts for custom validators, these can now be registered with the `@register_validator` in your own code as shown above in the [Pydantic Support](#pydantic-support) section. Custom validators registered in your own code will be picked up by Guardrails when you execute the `Guard` via `__call__` or `parse`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String Formatting In Prompts\n", + "Previously Guardrails used `f-string`'s and `str.format` to include prompt variables, prompt primitives, and other partials in the prompt sent to the LLM. This necessitated any braces, aka curly brackets, to be escaped with additional braces to prevent its contents from being considered as variable that should be substituted during string formatting.\n", + "\n", + "We now use `Template.safe_substitute` to avoid this issue completely. This does, however, require a change to how variables are expressed in string arguments such as prompts and instructions. Now instead of expressing a variable for the prompt as `{{my_var}}`, it should be expressed as `${my_var}`.\n", + "\n", + "In addition to this change in variable syntax, we have also started namespacing our prompt primitives. Whereas before you would use one of our built in primitives like so: `@complete_json_suffix_v2`, they should now be specified as `${gr.complete_json_suffix_v2}`.\n", + "\n", + "To highlight these changes in practice, below is the prompt section of a RAIL spec where each tab shows the respective before and after format.\n", + "\n", + "=== \"0.1.x\"\n", + " ```xml\n", + " \n", + " Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'.\n", + "\n", + " {document}\n", + "\n", + " @xml_prefix_prompt\n", + "\n", + " {{output_schema}}\n", + "\n", + " @json_suffix_prompt\n", + " \n", + " ```\n", + "=== \"0.2.x\"\n", + " ```xml\n", + " \n", + " Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'.\n", + "\n", + " ${document}\n", + "\n", + " ${gr.xml_prefix_prompt}\n", + "\n", + " ${output_schema}\n", + "\n", + " ${gr.json_suffix_prompt}\n", + " \n", + " ```\n", + "\n", + "### Replacing The Old Format With The New\n", + "#### String Variables\n", + "To easily find usage of the old format of string variables (i.e. `{document}` or `{{output_schema}}`) in your code, you can utilize the below regex:\n", + "\n", + "##### Find\n", + "`([{]+)([^}\"']*)([}]+)`\n", + "\n", + "##### Replace\n", + "`${$2}`\n", + "\n", + "#### Prompt Primitives\n", + "To easily find usage of the old format of prompt primitives (i.e. `@json_suffix_prompt`) in your code, you can utilize the below regex:\n", + "\n", + "##### Find\n", + "`(^[@])(\\w+)`\n", + "\n", + "##### Replace\n", + "`${gr.$2}`\n", + "\n", + "### Using Guardrails With LangChain\n", + "The changes to string format also effect the Guardrails AI integration with LangChain. Since LangChain supports `f-string`'s and `jinja2` for templating, and the `GuardrailsOutputParser` opts for the `f-string` option, any braces in the prompt must be escaped before instantiating the LangChain `PromptTemplate`. To make this easy, we added an `escape` method on our prompt class to handle this for you; you simply need to invoke it when passing the prompt to `PromptTemplate` like so:\n", + "```py\n", + "from langchain.output_parsers import GuardrailsOutputParser\n", + "from langchain.prompts import PromptTemplate\n", + "\n", + "output_parser = GuardrailsOutputParser.from_rail_string(rail_spec, api=openai.ChatCompletion.create)\n", + "\n", + "prompt = PromptTemplate(\n", + " template=output_parser.guard.prompt.escape(),\n", + " input_variables=output_parser.guard.prompt.variable_names,\n", + ")\n", + "```\n", + "\n", + "See the [LangChain integration docs](/integrations/langchain) for more details." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/api_reference/data_types.md b/docs/api_reference/data_types.md new file mode 100644 index 000000000..cceadf2c6 --- /dev/null +++ b/docs/api_reference/data_types.md @@ -0,0 +1,18 @@ + + +::: guardrails.datatypes + options: + filters: + - "!get_validators" + - "!registry" + - "!DataType" + - "!register_type" + - "!Scalar" + - "!set_children" + - "!validate" + - "!from_str" + - "!from_xml" + - "!model" + - "!validators" + - "!to_object_element" + show_bases: true diff --git a/docs/api_reference/document_store.md b/docs/api_reference/document_store.md new file mode 100644 index 000000000..f74456823 --- /dev/null +++ b/docs/api_reference/document_store.md @@ -0,0 +1 @@ +::: guardrails.document_store \ No newline at end of file diff --git a/docs/guard.md b/docs/api_reference/guard.md similarity index 66% rename from docs/guard.md rename to docs/api_reference/guard.md index 1b18fb6ba..854732d21 100644 --- a/docs/guard.md +++ b/docs/api_reference/guard.md @@ -6,5 +6,9 @@ members: - "from_rail" - "from_rail_string" + - "from_pydantic" + - "from_string" + - "configure" - "__call__" - "parse" + - "state" diff --git a/docs/api_reference/rail.md b/docs/api_reference/rail.md new file mode 100644 index 000000000..cab956f4b --- /dev/null +++ b/docs/api_reference/rail.md @@ -0,0 +1,13 @@ +::: guardrails.rail.Rail + options: + members: + - "from_file" + - "from_string" + - "from_xml" + - "from_pydantic" + - "from_string_validators" + - "input_schema" + - "output_schema" + - "instructions" + - "prompt" + - "version" \ No newline at end of file diff --git a/docs/api_reference/response_structures.md b/docs/api_reference/response_structures.md new file mode 100644 index 000000000..efac7dd9f --- /dev/null +++ b/docs/api_reference/response_structures.md @@ -0,0 +1,9 @@ + + +::: guardrails.validators + options: + members: + - "ValidationResult" + - "PassResult" + - "FailResult" + - "ValidatorError" diff --git a/docs/api_reference/schema.md b/docs/api_reference/schema.md new file mode 100644 index 000000000..f92f07b90 --- /dev/null +++ b/docs/api_reference/schema.md @@ -0,0 +1 @@ +::: guardrails.schema \ No newline at end of file diff --git a/docs/validation.md b/docs/api_reference/validators.md similarity index 66% rename from docs/validation.md rename to docs/api_reference/validators.md index 5a846db09..e5c748101 100644 --- a/docs/validation.md +++ b/docs/api_reference/validators.md @@ -10,4 +10,10 @@ - "!Validator" - "!validate" - "!register_validator" + - "!PydanticReAsk" + - "!Filter" + - "!Refrain" + - "!ValidationResult" + - "!PassResult" + - "!FailResult" - "!__*" diff --git a/docs/concepts/guard.md b/docs/concepts/guard.md new file mode 100644 index 000000000..81fac6000 --- /dev/null +++ b/docs/concepts/guard.md @@ -0,0 +1,109 @@ +# Guard + + +The guard object is the main interface for GuardRails. It is seeded with a RailSpec, and then used to run the GuardRails AI engine. It is the object that accepts changing prompts, wraps LLM prompts, and keeps track of call history. + + +## How it works + +```mermaid +graph + A[Create `RAIL` spec] --> B[Initialize `guard` from spec]; + C[Collect LLM callable information] --> D[Invoke `guard`]; + B --> D; + E[Invoke guard with prompt and instructions] --> D; + D --> F[Guard invokes LLM API]; + F --> G[LLM API returns]; + G --> H[LLM metadata is stored and logged]; + H --> I[LLM output is validated]; + I --> J[Valid]; + I --> K[Invalid]; + K --> L[Check the on-fail action set in failed validator]; + L --> M[default: no-op]; + M --> N[Return output]; + L --> O[reask: reask LLM]; + O --> F; + L --> P[filter: filter out output]; + P --> N; + L --> Q[fix: fix output based on validator fix function] + Q --> N; + J --> N; +``` + +## Two main flows +### __call__ +After instantiating a Guard you can call it in order to wrap an LLM with Guardrails and validate the output of that LLM according to the RAIL specification you provided. Calling the guard in this way returns a tuple with the raw output from the LLM first, and the validatd output second. +```py +import openai +from guardrails import Guard + +guard = Guard.from_rail(...) + +raw_output, validated_output = guard( + openai.Completion.create, + engine="text-davinci-003", + max_tokens=1024, + temperature=0.3 +) + +print(raw_output) +print(validated_output) +``` + +### parse +If you would rather call the LLM yourself, or at least make the first call yourself, you can use `Guard.parse` to apply your RAIL specification to the LLM output as a post process. You can also allow Guardrails to make re-asks to the LLM by specifying the `num_reasks` argument, or keep it purely as a post-processor by setting it to zero. Unlike `__call__`, `Guard.parse` only returns the validated output. + +Calling `Guard.parse` with reasks: +```py +import openai +from guardrails import Guard + +guard = Guard.from_rail(...) + +output = call_my_llm() + +validated_output = guard.parse( + llm_output=output, + llm_api=openai.Completion.create, + engine="text-davinci-003", + max_tokens=1024, + temperature=0.3, + num_reasks=2 +) + +print(validated_output) +``` + +Calling `Guard.parse` as a post-processor: +```py +import openai +from guardrails import Guard + +guard = Guard.from_rail(...) + +output = call_my_llm() + +validated_output = guard.parse( + llm_output=output, + num_reasks=0 +) + +print(validated_output) +``` + +## Error Handling and Retries +GuardRails currently performs automatic retries with exponential backoff when any of the following errors occur when calling the LLM: + +- openai.error.APIConnectionError +- openai.error.APIError +- openai.error.TryAgain +- openai.error.Timeout +- openai.error.RateLimitError +- openai.error.ServiceUnavailableError + +Note that this list is not exhaustive of the possible errors that could occur. In the event that errors other than these arise during LLM calls, an exception will be raised. The messaging of this exception is intended to help troubleshoot common problems, especially with custom LLM wrappers, as well as communicate the underlying error. This type of exception would look like the following: +```log +The callable `fn` passed to `Guard(fn, ...)` failed with the following error: `{Root error message here!}`. Make sure that `fn` can be called as a function that takes in a single prompt string and returns a string. +``` + +In situations where the exception can be handled and retried, that is the exception is in the list above, the call to the LLM will be retried with exponential backoff until a max wait time between requests of sixty (60) seconds is reached. \ No newline at end of file diff --git a/docs/rail/instructions.md b/docs/concepts/instructions.md similarity index 64% rename from docs/rail/instructions.md rename to docs/concepts/instructions.md index 0d9a7e063..8db9a1cfe 100644 --- a/docs/rail/instructions.md +++ b/docs/concepts/instructions.md @@ -8,9 +8,9 @@ In addition to the high level task description, the prompt also contains the fol | Component | Syntax | Description | |-------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Variables | `{{variable_name}}` | These are provided by the user at runtime, and substituted in the prompt. | -| Output Schema | `{output_schema}` | This is the schema of the expected output, and is compiled based on the `output` element. For more information on how the output schema is compiled for the prompt, check out [`output` element compilation](../output/#adding-compiled-output-element-to-prompt). | -| Prompt Primitives | `@prompt_primitive_name` | These are pre-constructed prompts that are useful for common tasks. E.g., some primitives may contain information that helps the LLM understand the output schema better. To see the full list of prompt primitives, check out [`guardrails/constants.xml`](https://github.com/ShreyaR/guardrails/blob/main/guardrails/constants.xml). | +| Variables | `${variable_name}` | These are provided by the user at runtime, and substituted in the prompt. | +| Output Schema | `${output_schema}` | This is the schema of the expected output, and is compiled based on the `output` element. For more information on how the output schema is compiled for the prompt, check out [`output` element compilation](../output/#adding-compiled-output-element-to-prompt). | +| Prompt Primitives | `${gr.prompt_primitive_name}` | These are pre-constructed prompts that are useful for common tasks. E.g., some primitives may contain information that helps the LLM understand the output schema better. To see the full list of prompt primitives, check out [`guardrails/constants.xml`](https://github.com/ShreyaR/guardrails/blob/main/guardrails/constants.xml). | ```xml @@ -18,14 +18,14 @@ In addition to the high level task description, the prompt also contains the fol You are a helpful assistant only capable of communicating with valid JSON, and no other text. -@json_suffix_prompt_examples +${gr.json_suffix_prompt_examples} ``` 1. The prompt contains high level task information. -2. `@json_suffix_prompt_examples` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: `Given below is XML that describes the information to extract from this document and the tags to extract it into.` +2. `${gr.json_suffix_prompt_examples}` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: `Given below is XML that describes the information to extract from this document and the tags to extract it into.` ``` You are a helpful assistant only capable of communicating with valid JSON, and no other text. diff --git a/docs/concepts/logs.md b/docs/concepts/logs.md new file mode 100644 index 000000000..c010ad90f --- /dev/null +++ b/docs/concepts/logs.md @@ -0,0 +1,78 @@ +# Inspecting logs + +All `gd.Guard` calls are logged internally, and can be accessed via two methods, `gd.Guard.guard_state` or `guardrails.log`. + +## 🪵 Accessing logs via `guardrails.log` + +This is the simplest way to access logs. It returns a list of all `gd.Guard` calls, in the order they were made. + +In order to access logs, run: + +```bash + +eliot-tree --output-format=ascii guardrails.log + +``` + +## 🇻🇦 Accessing logs via `gd.Guard.guard_state` + +`guard_state` is an attribute of the `gd.Guard` class. It contains: + +1. A list of all `gd.Guard` calls, in the order they were made. +2. For each call, reasks needed and their results. + +To pretty print logs, run: + +```python +from rich import print + +print(guard.state.most_recent_call.tree) +``` + +![guard_state](img/guard_history.png) + +To access fine-grained logs on field validation, see the FieldValidationLogs object: + +```python +validation_logs = guard.guard_state.all_histories[0].history[0].field_validation_logs +print(validation_logs.json(indent=2)) +``` + +```json +{ + "validator_logs": [], + "children": { + "name": { + "validator_logs": [ + { + "validator_name": "TwoWords", + "value_before_validation": "peter parker the second", + "validation_result": { + "outcome": "fail", + "metadata": null, + "error_message": "must be exactly two words", + "fix_value": "peter parker" + }, + "value_after_validation": { + "incorrect_value": "peter parker the second", + "fail_results": [ + { + "outcome": "fail", + "metadata": null, + "error_message": "must be exactly two words", + "fix_value": "peter parker" + } + ], + "path": [ + "name" + ] + } + } + ], + "children": {} + } + } +} + + +``` \ No newline at end of file diff --git a/docs/rail/output.md b/docs/concepts/output.md similarity index 100% rename from docs/rail/output.md rename to docs/concepts/output.md diff --git a/docs/rail/prompt.md b/docs/concepts/prompt.md similarity index 51% rename from docs/rail/prompt.md rename to docs/concepts/prompt.md index fc819e422..dccc37d58 100644 --- a/docs/rail/prompt.md +++ b/docs/concepts/prompt.md @@ -8,9 +8,9 @@ In addition to the high level task description, the prompt also contains the fol | Component | Syntax | Description | |-------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Variables | `{{variable_name}}` | These are provided by the user at runtime, and substituted in the prompt. | -| Output Schema | `{output_schema}` | This is the schema of the expected output, and is compiled based on the `output` element. For more information on how the output schema is compiled for the prompt, check out [`output` element compilation](../output/#adding-compiled-output-element-to-prompt). | -| Prompt Primitives | `@prompt_primitive_name` | These are pre-constructed prompts that are useful for common tasks. E.g., some primitives may contain information that helps the LLM understand the output schema better. To see the full list of prompt primitives, check out [`guardrails/constants.xml`](https://github.com/ShreyaR/guardrails/blob/main/guardrails/constants.xml). | +| Variables | `${variable_name}` | These are provided by the user at runtime, and substituted in the prompt. | +| Output Schema | `${output_schema}` | This is the schema of the expected output, and is compiled based on the `output` element. For more information on how the output schema is compiled for the prompt, check out [`output` element compilation](../output/#adding-compiled-output-element-to-prompt). | +| Prompt Primitives | `${gr.prompt_primitive_name}` | These are pre-constructed prompts that are useful for common tasks. E.g., some primitives may contain information that helps the LLM understand the output schema better. To see the full list of prompt primitives, check out [`guardrails/constants.xml`](https://github.com/ShreyaR/guardrails/blob/main/guardrails/constants.xml). | ```xml @@ -18,26 +18,26 @@ In addition to the high level task description, the prompt also contains the fol Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'. -{document} +${document} -@xml_prefix_prompt +${gr.xml_prefix_prompt} -{{output_schema}} +${output_schema} -@json_suffix_prompt +${gr.json_suffix_prompt} ``` 1. The prompt contains high level task information. -2. The variable `{{document}}` is provided by the user at runtime. -3. `@xml_prefix_prompt` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: `Given below is XML that describes the information to extract from this document and the tags to extract it into.` -4. `{output_schema}` is the output schema and contains information about , which is compiled based on the `output` element. -5. `@json_suffix_prompt` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: +2. The variable `${document}` is provided by the user at runtime. +3. `${gr.xml_prefix_prompt}` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: `Given below is XML that describes the information to extract from this document and the tags to extract it into.` +4. `${output_schema}` is the output schema and contains information about , which is compiled based on the `output` element. +5. `${gr.json_suffix_prompt}` is a prompt primitive provided by guardrails. It is equivalent to typing the following lines in the prompt: ``` ONLY return a valid JSON object (no other text is necessary). The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. diff --git a/docs/concepts/pydantic_xml_and_strings.md b/docs/concepts/pydantic_xml_and_strings.md new file mode 100644 index 000000000..e915cef71 --- /dev/null +++ b/docs/concepts/pydantic_xml_and_strings.md @@ -0,0 +1,46 @@ +# Pydantic, XML, and Strings + +Guardrails is currently only available in python, but we do want to expand to other languages as demand becomes apparent. + +As you've seen in other concepts pages, xml and pydantic are used to model RailSpecs. We recommend using pydantic instead of xml. The modeling capabilities are easier to use and more powerful, and it integrates more seamlessly with a python codebase. All examples in these docs have representation for both xml and pydantic. + +Guardrails additionally supports single-field, string outputs. This is useful for running validations on simple, single-value outputs. + +### Simple Example + +In this simple example, we initialize a guard that validates a string output and ensures that it is within 10-20 characters. + +=== "XML" + + ```py + from guardrails import Guard + railspec = """ + + + + """ + + guard = Guard.from_rail_string(railspec) + ``` + +=== "Pydantic" + + N/A - Pydantic models can only be used to reprensent structured output (i.e. JSON). + +=== "Single-Field String" + + ```py + from guardrails import Guard + from guardrails.validators import ValidLength + + validators = [ValidLength(10, 20)] + + guard = Guard.from_string( + validators=validators + description="Puppy name" + ) + ``` diff --git a/docs/rail/index.md b/docs/concepts/rail.md similarity index 74% rename from docs/rail/index.md rename to docs/concepts/rail.md index aa9f58757..ab946336a 100644 --- a/docs/rail/index.md +++ b/docs/concepts/rail.md @@ -33,41 +33,13 @@ Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'. -{document} +${document} -@xml_prefix_prompt +${gr.xml_prefix_prompt} -{{output_schema}} +${output_schema} -@json_suffix_prompt - - - +${gr.json_suffix_prompt} ``` @@ -80,8 +52,7 @@ Given the following document, answer the following questions. If the answer does 2. **Simple and familiar:** `RAIL` should be familiar to anyone familiar with HTML, and should be easy to learn. 3. **Validation and correction**: `RAIL` can be used to define quality criteria for the expected output, and corrective actions to take in case the quality criteria is not met. 4. **Can define complex structures:** `RAIL` can be used to define arbitrarily complex structures, such as nested lists, nested objects, etc. -5. **Supports writing custom code:** If needed, `RAIL` supports writing code for using validators, custom corrective actions, etc. To see examples of this, check out the [RAIL Script](script.md) page. -6. **Code assistance**: In the future, we plan to support code completion and IntelliSense for `RAIL` specifications, which will make it very easy to write `RAIL` specifications. +5. **Code assistance**: In the future, we plan to support code completion and IntelliSense for `RAIL` specifications, which will make it very easy to write `RAIL` specifications. **Design inspiration** @@ -90,13 +61,12 @@ Given the following document, answer the following questions. If the answer does ## 📚 Components of an `RAIL` Specification -The `RAIL` specification contains 3 main components: +The `RAIL` specification contains 2 main components: 1. `Output`: Contains information about the expected output of the LLM. It contains the spec for the overall structure of the LLM output, type info for each field, and the quality criteria for each field and the corrective action to be taken in case quality criteria is not met. This is the main component of the `RAIL` specification, which enforces the guarantees that the LLM should provide. Check out the [RAIL Output](output.md) page for more details, including the full specifcation of how to create complex output schemas. 2. `Prompt`: Prompt template, and contains the high level instructions that are sent to the LLM. Check out the [RAIL Prompt](prompt.md) page for more details. -3. (Experimental) (Optional) `Script`: Contains any custom code for implementing the schema. This is useful for implementing custom validators, custom corrective actions, etc. Check out the [RAIL Script](script.md) page for more details. Let's see an example of an `RAIL` specification in action: @@ -112,17 +82,11 @@ Let's see an example of an `RAIL` specification in action: ... - - - ``` 1. The `output` element contains the structure of the expected output of the LLM. It contains the spec for the overall structure of the LLM output, type info for each field, and the quality criteria for each field and the corrective action to be taken in case quality criteria is not met. 2. The `prompt` element contains the high level instructions that are sent to the LLM. Check out the [RAIL Prompt](prompt.md) page for more details. -3. The `script` element is optional, and contains any custom code for implementing the schema. ## 📖 How to use `RAIL` in Guardrails? diff --git a/docs/concepts/railspec.md b/docs/concepts/railspec.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/concepts/validators.md b/docs/concepts/validators.md new file mode 100644 index 000000000..57da2618d --- /dev/null +++ b/docs/concepts/validators.md @@ -0,0 +1,143 @@ +# Validators + +Validators are how we apply quality controls to the schemas specified in our `RAIL` specs. They specify the criteria to measure whether an output is valid, as well as what actions to take when an output does not meet those criteria. + +## How do Validators work? +When a validator is applied to a property on a schema, and output is provided for that schema, either by wrapping the LLM call or passing in the LLM output, the validators are executed against the values for the properties they were applied to. If the value for the property passes the criteria defined, a `PassResult` is returned from the validator. This `PassResult` tells Guardrails to treat the value as if it is valid. In most cases this means returning that value for that property at the end; other advanced cases, like using a value override, will be covered in other sections. If, however, the value for the property does not pass the criteria, a `FailResult` is returned. This in turn tells Guardrails to take any corrective actions defined for the property and validation. Corrective actions are defined by the `on-fail-...` attributes in a `RAIL` spec. You can read more about what corrective actions are available [here](/rail/output/#specifying-corrective-actions). + +## Validator Structure +### Arguments +Now we know that a validator is some method that checks the value of a given schema property. By this definition we can assume that the validator uses that schema property's value at runtime. In some cases the validator may require other arguments to successfully execute. These arguments are defined in the validator's method signature and assigned values in the `RAIL` spec. For example, the `ValidLength` (aka `length`) validator needs to know the bounds of the sized object (string, list, dict, etc.). The bounds are specified by a `min` and `max` argument. + +This means that the `ValidLength`'s constructor accepts these arguments so they can be used during validation later: +```python +@register_validator(name="length", data_type=["string", "list"]) +class ValidLength(Validator): + def __init__( + self, min: int = None, max: int = None, on_fail: Optional[Callable] = None + ): + # ... +``` + +The values for these arguments are provided when assigning this validator within a `RAIL` spec as positional arguments. + +For example: +```xml + +``` +will apply the `ValidLength` formater to the string output requiring a minimum character count of 1 and a max of 120. + +### Metadata +Sometimes validators need addtional parameters that are only availble during runtime. This is where metadata comes in. Metadata could be data generated during the execution of a validtor (*important if you're writing your own validators*), or could just be a container for runtime arguments. For example, the `ExtractedSummarySentencesMatch` validator accepts a `filepaths` property in the metadata dictionary to specify what source files to compare the summary against to ensure similarity. Unlike arguments which are specified in the `RAIL` spec, metadata is specified when calling the guard: +```python +guard = Guard.from_rail("my_railspec.rail") + +raw_output, guarded_output = guard( + llm_api=openai.ChatCompletion.create, + model="gpt-3.5-turbo", + num_reasks=3, + metadata={ + "filepaths": [ + "./my_data/article1.txt", + "./my_data/article2.txt", + ] + } +) +``` + +#### How do I know what metadata is required? +First step is to check the docs. Each validator has an API reference that documents both its initialization arguments and any required metadata that must be supplied at runtime. Continuing with the example used above, `ExtractedSummarySentencesMatch` accepts an optional threshold argument which defaults to `0.7`; it also requires an entry in the metadata called `filepaths` which is an array of strings specifying which documents to use for the similarity comparison. You can see an example of a Validator's metadata documentation [here](/validation/#guardrails.validators.ExtractedSummarySentencesMatch). + +Secondly, if a piece of metadata is required and not present, a `RuntimeError` will be raised. For example, if the metadata requirements are not met for the above validator, an `RuntimeError` will be raised with the following message: + +> extracted-sentences-summary-match validator expects `filepaths` key in metadata + +## Custom Validators +If you need to perform a validation that is not currently supported by the [validators](/validation) included in guardrails, you can create your own custom validators to be used in your local python environment. + +A custom validator can be as simple as a single function if you do not require addtional arguments: +```py +from typing import Dict +from guardrails.validators import ( + FailResult, + PassResult, + register_validator, + ValidationResult +) + +@register_validator(name="starts-with-a", data_type="string") +def starts_with_a(value: str, metadata: Dict) -> ValidationResult: + if value.startswith("a"): + return PassResult(metadata) + + return FailResult( + error_message=f"Value {value} does not start with a." + ) + +``` + +If you need to perform more complex operations or require addtional arguments to perform the validation, then the validator can be specified as a class that inherits from our base Validator class: +```py +from typing import Callable, Dict, Optional +from guardrails.validators import ( + FailResult, + PassResult, + register_validator, + ValidationResult, + Validator +) + +@register_validator(name="starts-with", data_type="string") +class StartsWith(Validator): + def __init__(self, prefix: str, on_fail: Optional[Callable] = None): + super().__init__(on_fail=on_fail, prefix=prefix) + self.prefix = prefix + + def validate(value: str, metadata: Dict) -> ValidationResult: + if value.startswith(self.prefix): + return PassResult(metadata) + + return FailResult( + error_message=f"Value {value} does not start with {self.prefix}.", + fix_value: f"{self.prefix}{value}" # To enable the "fix" option for on-fail + ) +``` + +Custom validators must be defined before creating a `Guard` or `RAIL` spec in the code, but otherwise can be used like built in validators: +```py +import openai +from guardrails import Guard +from .my_custom_validators import starts_with_a, StartsWith + +rail_str = """ + + + + + + + + + +Generate a dataset of fake word pairs within a JSON object. +The "a-string" property should start with the letter "a", +and the "custom-string" property should start with "my-prefix". + +${guardrails.complete_json_suffix} + + + +""" + +guard = Guard.from_rail_string(rail_string=rail_str) + +raw_output, guarded_output = guard( + llm_api=openai.ChatCompletion.create, + model="gpt-3.5-turbo" +) + +print("validated output: ", guarded_output) +``` \ No newline at end of file diff --git a/docs/examples/bug_free_python_code.ipynb b/docs/examples/bug_free_python_code.ipynb index 7de730396..a290e44e3 100644 --- a/docs/examples/bug_free_python_code.ipynb +++ b/docs/examples/bug_free_python_code.ipynb @@ -26,7 +26,14 @@ "\n", "## Step 1: Generating `RAIL` Spec\n", "\n", - "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string." + "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string. We will also show the same RAIL spec in a code-first format using a Pydantic model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "XML option:" ] }, { @@ -51,14 +58,46 @@ "Given the following high level leetcode problem description, write a short Python code snippet that solves the problem.\n", "\n", "Problem Description:\n", - "{{leetcode_problem}}\n", + "${leetcode_problem}\n", "\n", - "@complete_json_suffix\n", + "${gr.complete_json_suffix}\n", "\n", "\n", "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pydantic model option:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "from guardrails.validators import BugFreePython\n", + "from guardrails.datatypes import PythonCode\n", + "\n", + "prompt = \"\"\"\n", + "Given the following high level leetcode problem description, write a short Python code snippet that solves the problem.\n", + "\n", + "Problem Description:\n", + "${leetcode_problem}\n", + "\n", + "${gr.complete_json_suffix}\"\"\"\n", + "\n", + "class BugFreePythonCode(BaseModel):\n", + " python_code: PythonCode = Field(validators=[BugFreePython(on_fail=\"reask\")])\n", + "\n", + " class Config:\n", + " arbitrary_types_allowed = True" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -75,17 +114,47 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import guardrails as gd\n", "\n", - "from rich import print\n", - "\n", + "from rich import print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From XML:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from the pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=BugFreePythonCode, prompt=prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -96,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -107,30 +176,27 @@ "problem.\n", "\n", "Problem Description:\n", - "{leetcode_problem}\n", + "${leetcode_problem}\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "<output>\n", - " <pythoncode name=\"python_code\" format=\"bug-free\"/>\n", + " <pythoncode name=\"python_code\" format=\"bug-free-python\"/>\n", "</output>\n", "\n", "\n", - "\n", - "\n", "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n", "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n", "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n", - "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n", + "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n", "\n", "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n", - "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n", - "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n", + "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n", + "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n", "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n", - "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n", + "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", "\n", - "JSON Object:\n", "\n" ], "text/plain": [ @@ -139,30 +205,27 @@ "problem.\n", "\n", "Problem Description:\n", - "\u001b[1m{\u001b[0mleetcode_problem\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mleetcode_problem\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", - "\n", - "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", - "\n", - "JSON Object:\n" + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\n" ] }, "metadata": {}, @@ -185,7 +248,15 @@ "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "import openai\n", "\n", @@ -214,7 +285,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -255,7 +326,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -300,7 +371,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -342,7 +413,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/extracting_entities.ipynb b/docs/examples/extracting_entities.ipynb index 386bf5d5b..eee33134c 100644 --- a/docs/examples/extracting_entities.ipynb +++ b/docs/examples/extracting_entities.ipynb @@ -25,7 +25,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -77,7 +77,7 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model.\n", "\n", "Here, we request:\n", "\n", @@ -85,9 +85,16 @@ "2. A object (i.e. key-value pairs) for the interest." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "XML option:" + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -111,18 +118,57 @@ "\n", "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'.\n", "\n", - "{{document}}\n", + "${document}\n", "\n", - "@xml_prefix_prompt\n", + "${gr.xml_prefix_prompt}\n", "\n", - "{output_schema}\n", + "${output_schema}\n", "\n", - "@json_suffix_prompt_v2_wo_none\n", + "${gr.json_suffix_prompt_v2_wo_none}\n", "\n", "\n", "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pydantic model option:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from guardrails.validators import LowerCase, TwoWords, OneLine\n", + "from pydantic import BaseModel, Field\n", + "from typing import List\n", + "\n", + "prompt = \"\"\"\n", + "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter 'None'.\n", + "\n", + "${document}\n", + "\n", + "${gr.xml_prefix_prompt}\n", + "\n", + "${output_schema}\n", + "\n", + "${gr.json_suffix_prompt_v2_wo_none}\"\"\"\n", + "\n", + "class Fee(BaseModel):\n", + " index: int = Field(validators=[(\"1-indexed\", \"noop\")])\n", + " name: str = Field(validators=[LowerCase(on_fail=\"fix\"), TwoWords(on_fail=\"reask\")])\n", + " explanation: str = Field(validators=[OneLine()])\n", + " value: float = Field(validators=[(\"percentage\", \"noop\")])\n", + "\n", + "class CreditCardAgreement(BaseModel):\n", + " fees: List[Fee] = Field(description=\"What fees and charges are associated with my account?\")\n", + " interest_rates: dict = Field(description=\"What are the interest rates offered by the bank on savings and checking accounts, loans, and credit products?\")" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -137,18 +183,25 @@ "3. Compiles the schema and type info from the RAIL spec and adds it to the prompt." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From XML:" + ] + }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator 1-indexed is not valid for element integer.\n", + "/home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages/guardrails/schema.py:218: UserWarning: Validator 1-indexed is not valid for element integer.\n", " warnings.warn(\n", - "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator percentage is not valid for element float.\n", + "/home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages/guardrails/schema.py:218: UserWarning: Validator percentage is not valid for element float.\n", " warnings.warn(\n" ] } @@ -157,6 +210,22 @@ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From Pydantic:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement, prompt=prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -169,7 +238,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -179,7 +248,7 @@ "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", "'None'.\n", "\n", - "{document}\n", + "${document}\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", @@ -203,8 +272,6 @@ "ONLY return a valid JSON object (no other text is necessary). The JSON MUST conform to the XML format, including \n", "any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise.\n", "\n", - "JSON Output:\n", - "\n", "\n" ], "text/plain": [ @@ -212,7 +279,7 @@ "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", "\u001b[32m'None'\u001b[0m.\n", "\n", - "\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", @@ -235,8 +302,6 @@ "\n", "ONLY return a valid JSON object \u001b[1m(\u001b[0mno other text is necessary\u001b[1m)\u001b[0m. The JSON MUST conform to the XML format, including \n", "any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise.\n", - "\n", - "JSON Output:\n", "\n" ] }, @@ -258,9 +323,18 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n", + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "import openai\n", "\n", @@ -285,7 +359,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -296,9 +370,9 @@ " {'index': 1, 'name': 'annual membership', 'explanation': 'Annual Membership Fee', 'value': 0},\n", " {\n", " 'index': 2,\n", - " 'name': 'my-chase-plan',\n", + " 'name': 'my chase',\n", " 'explanation': 'My Chase Plan Fee (fixed finance charge)',\n", - " 'value': 1.72\n", + " 'value': 0.0172\n", " },\n", " {\n", " 'index': 3,\n", @@ -306,31 +380,49 @@ " 'explanation': 'Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer, \n", "whichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5% of the \n", "amount of each transfer, whichever is greater.',\n", - " 'value': 3\n", + " 'value': 0.05\n", " },\n", " {\n", " 'index': 4,\n", " 'name': 'cash advances',\n", " 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\n", - " 'value': 5\n", + " 'value': 0.05\n", " },\n", " {\n", " 'index': 5,\n", " 'name': 'foreign transactions',\n", " 'explanation': 'Foreign Transactions 3% of the amount of each transaction in U.S. dollars.',\n", - " 'value': 3\n", + " 'value': 0.03\n", " },\n", " {'index': 6, 'name': 'late payment', 'explanation': 'Late Payment Up to $40.', 'value': 0},\n", - " {'index': 7, 'name': 'over-credit-limit', 'explanation': 'Over-the-Credit-Limit None', 'value': 0},\n", + " {'index': 7, 'name': 'over-the-credit-limit', 'explanation': 'Over-the-Credit-Limit None', 'value': 0},\n", " {'index': 8, 'name': 'return payment', 'explanation': 'Return Payment Up to $40.', 'value': 0},\n", " {'index': 9, 'name': 'return check', 'explanation': 'Return Check None', 'value': 0}\n", " ],\n", " 'interest_rates': {\n", - " 'purchase': {'apr': 0, 'after_apr': 19.49},\n", - " 'my_chase_loan': {'apr': 19.49},\n", - " 'balance_transfer': {'apr': 0, 'after_apr': 19.49},\n", - " 'cash_advance': {'apr': 29.49},\n", - " 'penalty': {'apr': 0, 'maximum_apr': 29.99}\n", + " 'purchase': {\n", + " 'apr': 0,\n", + " 'explanation': 'Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months that your \n", + "Account is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'\n", + " },\n", + " 'my_chase_loan': {\n", + " 'apr': 19.49,\n", + " 'explanation': 'My Chase Loan SM APR 19.49%. This APR will vary with the market based on the Prime \n", + "Rate.'\n", + " },\n", + " 'balance_transfer': {\n", + " 'apr': 0,\n", + " 'explanation': 'Balance Transfer APR 0% Intro APR for the first 18 months that your Account is open. \n", + "After that, 19.49%. This APR will vary with the market based on the Prime Rate.'\n", + " },\n", + " 'cash_advance': {\n", + " 'apr': 29.49,\n", + " 'explanation': 'Cash Advance APR 29.49%. This APR will vary with the market based on the Prime Rate.'\n", + " },\n", + " 'penalty': {\n", + " 'apr': 29.99,\n", + " 'explanation': 'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'\n", + " }\n", " }\n", "}\n", "\n" @@ -341,9 +433,9 @@ " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'annual membership'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Annual Membership Fee'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'my-chase-plan'\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'my chase'\u001b[0m,\n", " \u001b[32m'explanation'\u001b[0m: \u001b[32m'My Chase Plan Fee \u001b[0m\u001b[32m(\u001b[0m\u001b[32mfixed finance charge\u001b[0m\u001b[32m)\u001b[0m\u001b[32m'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m1.72\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.0172\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", @@ -351,31 +443,49 @@ " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer, \u001b[0m\n", "\u001b[32mwhichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5% of the \u001b[0m\n", "\u001b[32mamount of each transfer, whichever is greater.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m3\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.05\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'cash advances'\u001b[0m,\n", " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Either $10 or 5% of the amount of each transaction, whichever is greater.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m5\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.05\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'foreign transactions'\u001b[0m,\n", " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Foreign Transactions 3% of the amount of each transaction in U.S. dollars.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m3\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.03\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'late payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Late Payment Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'over-credit-limit'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Over-the-Credit-Limit None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'over-the-credit-limit'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Over-the-Credit-Limit None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'return payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Return Payment Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'return check'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Return Check None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m,\n", " \u001b[32m'interest_rates'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'purchase'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'apr'\u001b[0m: \u001b[1;36m0\u001b[0m, \u001b[32m'after_apr'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[32m'my_chase_loan'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'apr'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[32m'balance_transfer'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'apr'\u001b[0m: \u001b[1;36m0\u001b[0m, \u001b[32m'after_apr'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[32m'cash_advance'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'apr'\u001b[0m: \u001b[1;36m29.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[32m'penalty'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'apr'\u001b[0m: \u001b[1;36m0\u001b[0m, \u001b[32m'maximum_apr'\u001b[0m: \u001b[1;36m29.99\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[32m'purchase'\u001b[0m: \u001b[1m{\u001b[0m\n", + " \u001b[32m'apr'\u001b[0m: \u001b[1;36m0\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Purchase Annual Percentage Rate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mAPR\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 0% Intro APR for the first 18 months that your \u001b[0m\n", + "\u001b[32mAccount is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[32m'my_chase_loan'\u001b[0m: \u001b[1m{\u001b[0m\n", + " \u001b[32m'apr'\u001b[0m: \u001b[1;36m19.49\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'My Chase Loan SM APR 19.49%. This APR will vary with the market based on the Prime \u001b[0m\n", + "\u001b[32mRate.'\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[32m'balance_transfer'\u001b[0m: \u001b[1m{\u001b[0m\n", + " \u001b[32m'apr'\u001b[0m: \u001b[1;36m0\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Balance Transfer APR 0% Intro APR for the first 18 months that your Account is open. \u001b[0m\n", + "\u001b[32mAfter that, 19.49%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[32m'cash_advance'\u001b[0m: \u001b[1m{\u001b[0m\n", + " \u001b[32m'apr'\u001b[0m: \u001b[1;36m29.49\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Cash Advance APR 29.49%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[32m'penalty'\u001b[0m: \u001b[1m{\u001b[0m\n", + " \u001b[32m'apr'\u001b[0m: \u001b[1;36m29.99\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\n", + " \u001b[1m}\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -390,14 +500,14 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "├── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "├── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "│   │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "│   │ │                                                                                                         │ │\n",
        "│   │ │ Given the following document, answer the following questions. If the answer doesn't exist in the        │ │\n",
@@ -550,9 +660,17 @@
        "│   │ │ including any types and format requests e.g. requests for lists, objects and specific types. Be correct │ │\n",
        "│   │ │ and concise.                                                                                            │ │\n",
        "│   │ │                                                                                                         │ │\n",
-       "│   │ │ JSON Output:                                                                                            │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │ Json Output:                                                                                            │ │\n",
+       "│   │ │                                                                                                         │ │\n",
        "│   │ │                                                                                                         │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "│   │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "│   │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "│   │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "│   │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "│   │ │ {                                                                                                       │ │\n",
        "│   │ │     \"fees\": [                                                                                           │ │\n",
@@ -574,20 +692,20 @@
        "│   │ │             \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each        │ │\n",
        "│   │ │ transfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either │ │\n",
        "│   │ │ $5 or 5% of the amount of each transfer, whichever is greater.\",                                        │ │\n",
-       "│   │ │             \"value\": 3                                                                                  │ │\n",
+       "│   │ │             \"value\": 0.05                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             \"index\": 4,                                                                                 │ │\n",
        "│   │ │             \"name\": \"cash advances\",                                                                    │ │\n",
        "│   │ │             \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\", │ │\n",
-       "│   │ │             \"value\": 5                                                                                  │ │\n",
+       "│   │ │             \"value\": 0.05                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             \"index\": 5,                                                                                 │ │\n",
        "│   │ │             \"name\": \"foreign transactions\",                                                             │ │\n",
        "│   │ │             \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S.           │ │\n",
        "│   │ │ dollars.\",                                                                                              │ │\n",
-       "│   │ │             \"value\": 3                                                                                  │ │\n",
+       "│   │ │             \"value\": 0.03                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             \"index\": 6,                                                                                 │ │\n",
@@ -617,21 +735,28 @@
        "│   │ │     \"interest_rates\": {                                                                                 │ │\n",
        "│   │ │         \"purchase\": {                                                                                   │ │\n",
        "│   │ │             \"apr\": 0,                                                                                   │ │\n",
-       "│   │ │             \"after_apr\": 19.49                                                                          │ │\n",
+       "│   │ │             \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months  │ │\n",
+       "│   │ │ that your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime    │ │\n",
+       "│   │ │ Rate.\"                                                                                                  │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         \"my_chase_loan\": {                                                                              │ │\n",
-       "│   │ │             \"apr\": 19.49                                                                                │ │\n",
+       "│   │ │             \"apr\": 19.49,                                                                               │ │\n",
+       "│   │ │             \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on    │ │\n",
+       "│   │ │ the Prime Rate.\"                                                                                        │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         \"balance_transfer\": {                                                                           │ │\n",
        "│   │ │             \"apr\": 0,                                                                                   │ │\n",
-       "│   │ │             \"after_apr\": 19.49                                                                          │ │\n",
+       "│   │ │             \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account │ │\n",
+       "│   │ │ is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         \"cash_advance\": {                                                                               │ │\n",
-       "│   │ │             \"apr\": 29.49                                                                                │ │\n",
+       "│   │ │             \"apr\": 29.49,                                                                               │ │\n",
+       "│   │ │             \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the    │ │\n",
+       "│   │ │ Prime Rate.\"                                                                                            │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         \"penalty\": {                                                                                    │ │\n",
-       "│   │ │             \"apr\": 0,                                                                                   │ │\n",
-       "│   │ │             \"maximum_apr\": 29.99                                                                        │ │\n",
+       "│   │ │             \"apr\": 29.99,                                                                               │ │\n",
+       "│   │ │             \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"  │ │\n",
        "│   │ │         }                                                                                               │ │\n",
        "│   │ │     }                                                                                                   │ │\n",
        "│   │ │ }                                                                                                       │ │\n",
@@ -647,10 +772,16 @@
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'index': 2,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
+       "│   │ │             'name': FieldReAsk(                                                                         │ │\n",
        "│   │ │                 incorrect_value='my chase plan',                                                        │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='my chase',                                                                   │ │\n",
+       "│   │ │                 fail_results=[                                                                          │ │\n",
+       "│   │ │                     FailResult(                                                                         │ │\n",
+       "│   │ │                         outcome='fail',                                                                 │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         error_message='must be exactly two words',                                      │ │\n",
+       "│   │ │                         fix_value='my chase'                                                            │ │\n",
+       "│   │ │                     )                                                                                   │ │\n",
+       "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 1, 'name']                                                                │ │\n",
        "│   │ │             ),                                                                                          │ │\n",
        "│   │ │             'explanation': 'My Chase Plan Fee (fixed finance charge)',                                  │ │\n",
@@ -662,20 +793,20 @@
        "│   │ │             'explanation': 'Balance Transfers Intro fee of either $5 or 3% of the amount of each        │ │\n",
        "│   │ │ transfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either │ │\n",
        "│   │ │ $5 or 5% of the amount of each transfer, whichever is greater.',                                        │ │\n",
-       "│   │ │             'value': 3                                                                                  │ │\n",
+       "│   │ │             'value': 0.05                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'index': 4,                                                                                 │ │\n",
        "│   │ │             'name': 'cash advances',                                                                    │ │\n",
        "│   │ │             'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.', │ │\n",
-       "│   │ │             'value': 5                                                                                  │ │\n",
+       "│   │ │             'value': 0.05                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'index': 5,                                                                                 │ │\n",
        "│   │ │             'name': 'foreign transactions',                                                             │ │\n",
        "│   │ │             'explanation': 'Foreign Transactions 3% of the amount of each transaction in U.S.           │ │\n",
        "│   │ │ dollars.',                                                                                              │ │\n",
-       "│   │ │             'value': 3                                                                                  │ │\n",
+       "│   │ │             'value': 0.03                                                                               │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'index': 6,                                                                                 │ │\n",
@@ -685,10 +816,16 @@
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'index': 7,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
+       "│   │ │             'name': FieldReAsk(                                                                         │ │\n",
        "│   │ │                 incorrect_value='over-the-credit-limit',                                                │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='over-the-credit-limit',                                                      │ │\n",
+       "│   │ │                 fail_results=[                                                                          │ │\n",
+       "│   │ │                     FailResult(                                                                         │ │\n",
+       "│   │ │                         outcome='fail',                                                                 │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         error_message='must be exactly two words',                                      │ │\n",
+       "│   │ │                         fix_value='over-the-credit-limit'                                               │ │\n",
+       "│   │ │                     )                                                                                   │ │\n",
+       "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 6, 'name']                                                                │ │\n",
        "│   │ │             ),                                                                                          │ │\n",
        "│   │ │             'explanation': 'Over-the-Credit-Limit None',                                                │ │\n",
@@ -708,16 +845,36 @@
        "│   │ │         }                                                                                               │ │\n",
        "│   │ │     ],                                                                                                  │ │\n",
        "│   │ │     'interest_rates': {                                                                                 │ │\n",
-       "│   │ │         'purchase': {'apr': 0, 'after_apr': 19.49},                                                     │ │\n",
-       "│   │ │         'my_chase_loan': {'apr': 19.49},                                                                │ │\n",
-       "│   │ │         'balance_transfer': {'apr': 0, 'after_apr': 19.49},                                             │ │\n",
-       "│   │ │         'cash_advance': {'apr': 29.49},                                                                 │ │\n",
-       "│   │ │         'penalty': {'apr': 0, 'maximum_apr': 29.99}                                                     │ │\n",
+       "│   │ │         'purchase': {                                                                                   │ │\n",
+       "│   │ │             'apr': 0,                                                                                   │ │\n",
+       "│   │ │             'explanation': 'Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months  │ │\n",
+       "│   │ │ that your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime    │ │\n",
+       "│   │ │ Rate.'                                                                                                  │ │\n",
+       "│   │ │         },                                                                                              │ │\n",
+       "│   │ │         'my_chase_loan': {                                                                              │ │\n",
+       "│   │ │             'apr': 19.49,                                                                               │ │\n",
+       "│   │ │             'explanation': 'My Chase Loan SM APR 19.49%. This APR will vary with the market based on    │ │\n",
+       "│   │ │ the Prime Rate.'                                                                                        │ │\n",
+       "│   │ │         },                                                                                              │ │\n",
+       "│   │ │         'balance_transfer': {                                                                           │ │\n",
+       "│   │ │             'apr': 0,                                                                                   │ │\n",
+       "│   │ │             'explanation': 'Balance Transfer APR 0% Intro APR for the first 18 months that your Account │ │\n",
+       "│   │ │ is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'               │ │\n",
+       "│   │ │         },                                                                                              │ │\n",
+       "│   │ │         'cash_advance': {                                                                               │ │\n",
+       "│   │ │             'apr': 29.49,                                                                               │ │\n",
+       "│   │ │             'explanation': 'Cash Advance APR 29.49%. This APR will vary with the market based on the    │ │\n",
+       "│   │ │ Prime Rate.'                                                                                            │ │\n",
+       "│   │ │         },                                                                                              │ │\n",
+       "│   │ │         'penalty': {                                                                                    │ │\n",
+       "│   │ │             'apr': 29.99,                                                                               │ │\n",
+       "│   │ │             'explanation': 'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'  │ │\n",
+       "│   │ │         }                                                                                               │ │\n",
        "│   │ │     }                                                                                                   │ │\n",
        "│   │ │ }                                                                                                       │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "└── ╭────────────────────────────────────────────────── Step 2 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ I was given the following JSON response, which had problems due to incorrect values.                    │ │\n",
@@ -725,18 +882,98 @@
        "    │ │ {                                                                                                       │ │\n",
        "    │ │   \"fees\": [                                                                                             │ │\n",
        "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 1,                                                                                       │ │\n",
+       "    │ │       \"name\": \"annual membership\",                                                                      │ │\n",
+       "    │ │       \"explanation\": \"Annual Membership Fee\",                                                           │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 2,                                                                                       │ │\n",
        "    │ │       \"name\": {                                                                                         │ │\n",
        "    │ │         \"incorrect_value\": \"my chase plan\",                                                             │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
+       "    │ │         \"error_messages\": [                                                                             │ │\n",
+       "    │ │           \"must be exactly two words\"                                                                   │ │\n",
+       "    │ │         ]                                                                                               │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       \"explanation\": \"My Chase Plan Fee (fixed finance charge)\",                                        │ │\n",
+       "    │ │       \"value\": 1.72                                                                                     │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 3,                                                                                       │ │\n",
+       "    │ │       \"name\": \"balance transfers\",                                                                      │ │\n",
+       "    │ │       \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer,    │ │\n",
+       "    │ │ whichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5%  │ │\n",
+       "    │ │ of the amount of each transfer, whichever is greater.\",                                                 │ │\n",
+       "    │ │       \"value\": 0.05                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 4,                                                                                       │ │\n",
+       "    │ │       \"name\": \"cash advances\",                                                                          │ │\n",
+       "    │ │       \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",       │ │\n",
+       "    │ │       \"value\": 0.05                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 5,                                                                                       │ │\n",
+       "    │ │       \"name\": \"foreign transactions\",                                                                   │ │\n",
+       "    │ │       \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S. dollars.\",      │ │\n",
+       "    │ │       \"value\": 0.03                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 6,                                                                                       │ │\n",
+       "    │ │       \"name\": \"late payment\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"Late Payment Up to $40.\",                                                         │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 7,                                                                                       │ │\n",
        "    │ │       \"name\": {                                                                                         │ │\n",
        "    │ │         \"incorrect_value\": \"over-the-credit-limit\",                                                     │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
+       "    │ │         \"error_messages\": [                                                                             │ │\n",
+       "    │ │           \"must be exactly two words\"                                                                   │ │\n",
+       "    │ │         ]                                                                                               │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       \"explanation\": \"Over-the-Credit-Limit None\",                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 8,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return payment\",                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Return Payment Up to $40.\",                                                       │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 9,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return check\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"Return Check None\",                                                               │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     }                                                                                                   │ │\n",
-       "    │ │   ]                                                                                                     │ │\n",
+       "    │ │   ],                                                                                                    │ │\n",
+       "    │ │   \"interest_rates\": {                                                                                   │ │\n",
+       "    │ │     \"purchase\": {                                                                                       │ │\n",
+       "    │ │       \"apr\": 0,                                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months that   │ │\n",
+       "    │ │ your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"my_chase_loan\": {                                                                                  │ │\n",
+       "    │ │       \"apr\": 19.49,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on the      │ │\n",
+       "    │ │ Prime Rate.\"                                                                                            │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"balance_transfer\": {                                                                               │ │\n",
+       "    │ │       \"apr\": 0,                                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account is    │ │\n",
+       "    │ │ open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"                  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"cash_advance\": {                                                                                   │ │\n",
+       "    │ │       \"apr\": 29.49,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the Prime    │ │\n",
+       "    │ │ Rate.\"                                                                                                  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"penalty\": {                                                                                        │ │\n",
+       "    │ │       \"apr\": 29.99,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"        │ │\n",
+       "    │ │     }                                                                                                   │ │\n",
+       "    │ │   }                                                                                                     │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ Help me correct the incorrect values based on the given error messages.                                 │ │\n",
@@ -747,9 +984,14 @@
        "    │ │ <output>                                                                                                │ │\n",
        "    │ │     <list name=\"fees\" description=\"What fees and charges are associated with my account?\">              │ │\n",
        "    │ │         <object>                                                                                        │ │\n",
+       "    │ │             <integer name=\"index\" format=\"1-indexed\"/>                                                  │ │\n",
        "    │ │             <string name=\"name\" format=\"lower-case; two-words\"/>                                        │ │\n",
+       "    │ │             <string name=\"explanation\" format=\"one-line\"/>                                              │ │\n",
+       "    │ │             <float name=\"value\" format=\"percentage\"/>                                                   │ │\n",
        "    │ │         </object>                                                                                       │ │\n",
        "    │ │     </list>                                                                                             │ │\n",
+       "    │ │     <object name=\"interest_rates\" description=\"What are the interest rates offered by the bank on       │ │\n",
+       "    │ │ savings and checking accounts, loans, and credit products?\"/>                                           │ │\n",
        "    │ │ </output>                                                                                               │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -759,26 +1001,118 @@
        "    │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere,     │ │\n",
        "    │ │ enter `null`.                                                                                           │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior:                          │ │\n",
-       "    │ │ - `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`                   │ │\n",
-       "    │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO',    │ │\n",
-       "    │ │ etc.]}}`                                                                                                │ │\n",
-       "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
-       "    │ │ format=\"1-indexed\" /></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`                    │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ JSON Object:                                                                                            │ │\n",
+       "    │ │ Json Output:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ You are a helpful assistant only capable of communicating with valid JSON, and no other text.           │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the │ │\n",
+       "    │ │ `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding  │ │\n",
+       "    │ │ XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g.        │ │\n",
+       "    │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere,     │ │\n",
+       "    │ │ enter `null`.                                                                                           │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior:                          │ │\n",
+       "    │ │ - `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`                     │ │\n",
+       "    │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO',     │ │\n",
+       "    │ │ etc.]}`                                                                                                 │ │\n",
+       "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
+       "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ No message history.                                                                                     │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
-       "    │ │                                                                                                         │ │\n",
        "    │ │ {                                                                                                       │ │\n",
        "    │ │   \"fees\": [                                                                                             │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"my-chase-plan\"                                                                           │ │\n",
+       "    │ │       \"index\": 1,                                                                                       │ │\n",
+       "    │ │       \"name\": \"annual membership\",                                                                      │ │\n",
+       "    │ │       \"explanation\": \"Annual Membership Fee\",                                                           │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 2,                                                                                       │ │\n",
+       "    │ │       \"name\": \"my chase plan\",                                                                          │ │\n",
+       "    │ │       \"explanation\": \"My Chase Plan Fee (fixed finance charge)\",                                        │ │\n",
+       "    │ │       \"value\": 0.0172                                                                                   │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"over-credit-limit\"                                                                       │ │\n",
+       "    │ │       \"index\": 3,                                                                                       │ │\n",
+       "    │ │       \"name\": \"balance transfers\",                                                                      │ │\n",
+       "    │ │       \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer,    │ │\n",
+       "    │ │ whichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5%  │ │\n",
+       "    │ │ of the amount of each transfer, whichever is greater.\",                                                 │ │\n",
+       "    │ │       \"value\": 0.05                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 4,                                                                                       │ │\n",
+       "    │ │       \"name\": \"cash advances\",                                                                          │ │\n",
+       "    │ │       \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",       │ │\n",
+       "    │ │       \"value\": 0.05                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 5,                                                                                       │ │\n",
+       "    │ │       \"name\": \"foreign transactions\",                                                                   │ │\n",
+       "    │ │       \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S. dollars.\",      │ │\n",
+       "    │ │       \"value\": 0.03                                                                                     │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 6,                                                                                       │ │\n",
+       "    │ │       \"name\": \"late payment\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"Late Payment Up to $40.\",                                                         │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 7,                                                                                       │ │\n",
+       "    │ │       \"name\": \"over-the-credit-limit\",                                                                  │ │\n",
+       "    │ │       \"explanation\": \"Over-the-Credit-Limit None\",                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 8,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return payment\",                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Return Payment Up to $40.\",                                                       │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     {                                                                                                   │ │\n",
+       "    │ │       \"index\": 9,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return check\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"Return Check None\",                                                               │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
+       "    │ │     }                                                                                                   │ │\n",
+       "    │ │   ],                                                                                                    │ │\n",
+       "    │ │   \"interest_rates\": {                                                                                   │ │\n",
+       "    │ │     \"purchase\": {                                                                                       │ │\n",
+       "    │ │       \"apr\": 0,                                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months that   │ │\n",
+       "    │ │ your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"my_chase_loan\": {                                                                                  │ │\n",
+       "    │ │       \"apr\": 19.49,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on the      │ │\n",
+       "    │ │ Prime Rate.\"                                                                                            │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"balance_transfer\": {                                                                               │ │\n",
+       "    │ │       \"apr\": 0,                                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account is    │ │\n",
+       "    │ │ open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"                  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"cash_advance\": {                                                                                   │ │\n",
+       "    │ │       \"apr\": 29.49,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the Prime    │ │\n",
+       "    │ │ Rate.\"                                                                                                  │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     \"penalty\": {                                                                                        │ │\n",
+       "    │ │       \"apr\": 29.99,                                                                                     │ │\n",
+       "    │ │       \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"        │ │\n",
        "    │ │     }                                                                                                   │ │\n",
-       "    │ │   ]                                                                                                     │ │\n",
+       "    │ │   }                                                                                                     │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
@@ -792,9 +1126,9 @@
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 2,                                                                                 │ │\n",
-       "    │ │             'name': 'my-chase-plan',                                                                    │ │\n",
+       "    │ │             'name': 'my chase',                                                                         │ │\n",
        "    │ │             'explanation': 'My Chase Plan Fee (fixed finance charge)',                                  │ │\n",
-       "    │ │             'value': 1.72                                                                               │ │\n",
+       "    │ │             'value': 0.0172                                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 3,                                                                                 │ │\n",
@@ -802,20 +1136,20 @@
        "    │ │             'explanation': 'Balance Transfers Intro fee of either $5 or 3% of the amount of each        │ │\n",
        "    │ │ transfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either │ │\n",
        "    │ │ $5 or 5% of the amount of each transfer, whichever is greater.',                                        │ │\n",
-       "    │ │             'value': 3                                                                                  │ │\n",
+       "    │ │             'value': 0.05                                                                               │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 4,                                                                                 │ │\n",
        "    │ │             'name': 'cash advances',                                                                    │ │\n",
        "    │ │             'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.', │ │\n",
-       "    │ │             'value': 5                                                                                  │ │\n",
+       "    │ │             'value': 0.05                                                                               │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 5,                                                                                 │ │\n",
        "    │ │             'name': 'foreign transactions',                                                             │ │\n",
        "    │ │             'explanation': 'Foreign Transactions 3% of the amount of each transaction in U.S.           │ │\n",
        "    │ │ dollars.',                                                                                              │ │\n",
-       "    │ │             'value': 3                                                                                  │ │\n",
+       "    │ │             'value': 0.03                                                                               │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 6,                                                                                 │ │\n",
@@ -825,7 +1159,7 @@
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 7,                                                                                 │ │\n",
-       "    │ │             'name': 'over-credit-limit',                                                                │ │\n",
+       "    │ │             'name': 'over-the-credit-limit',                                                            │ │\n",
        "    │ │             'explanation': 'Over-the-Credit-Limit None',                                                │ │\n",
        "    │ │             'value': 0                                                                                  │ │\n",
        "    │ │         },                                                                                              │ │\n",
@@ -843,11 +1177,31 @@
        "    │ │         }                                                                                               │ │\n",
        "    │ │     ],                                                                                                  │ │\n",
        "    │ │     'interest_rates': {                                                                                 │ │\n",
-       "    │ │         'purchase': {'apr': 0, 'after_apr': 19.49},                                                     │ │\n",
-       "    │ │         'my_chase_loan': {'apr': 19.49},                                                                │ │\n",
-       "    │ │         'balance_transfer': {'apr': 0, 'after_apr': 19.49},                                             │ │\n",
-       "    │ │         'cash_advance': {'apr': 29.49},                                                                 │ │\n",
-       "    │ │         'penalty': {'apr': 0, 'maximum_apr': 29.99}                                                     │ │\n",
+       "    │ │         'purchase': {                                                                                   │ │\n",
+       "    │ │             'apr': 0,                                                                                   │ │\n",
+       "    │ │             'explanation': 'Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months  │ │\n",
+       "    │ │ that your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime    │ │\n",
+       "    │ │ Rate.'                                                                                                  │ │\n",
+       "    │ │         },                                                                                              │ │\n",
+       "    │ │         'my_chase_loan': {                                                                              │ │\n",
+       "    │ │             'apr': 19.49,                                                                               │ │\n",
+       "    │ │             'explanation': 'My Chase Loan SM APR 19.49%. This APR will vary with the market based on    │ │\n",
+       "    │ │ the Prime Rate.'                                                                                        │ │\n",
+       "    │ │         },                                                                                              │ │\n",
+       "    │ │         'balance_transfer': {                                                                           │ │\n",
+       "    │ │             'apr': 0,                                                                                   │ │\n",
+       "    │ │             'explanation': 'Balance Transfer APR 0% Intro APR for the first 18 months that your Account │ │\n",
+       "    │ │ is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'               │ │\n",
+       "    │ │         },                                                                                              │ │\n",
+       "    │ │         'cash_advance': {                                                                               │ │\n",
+       "    │ │             'apr': 29.49,                                                                               │ │\n",
+       "    │ │             'explanation': 'Cash Advance APR 29.49%. This APR will vary with the market based on the    │ │\n",
+       "    │ │ Prime Rate.'                                                                                            │ │\n",
+       "    │ │         },                                                                                              │ │\n",
+       "    │ │         'penalty': {                                                                                    │ │\n",
+       "    │ │             'apr': 29.99,                                                                               │ │\n",
+       "    │ │             'explanation': 'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'  │ │\n",
+       "    │ │         }                                                                                               │ │\n",
        "    │ │     }                                                                                                   │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
@@ -856,7 +1210,7 @@
       ],
       "text/plain": [
        "Logs\n",
-       "├── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "├── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "│   │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mGiven the following document, answer the following questions. If the answer doesn't exist in the \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1009,9 +1363,17 @@
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mincluding any types and format requests e.g. requests for lists, objects and specific types. Be correct\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mand concise.\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"fees\": [\u001b[0m\u001b[48;2;245;245;220m                                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -1033,20 +1395,20 @@
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each \u001b[0m\u001b[48;2;245;245;220m      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtransfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m$5 or 5% of the amount of each transfer, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 3\u001b[0m\u001b[48;2;245;245;220m                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 0.05\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"cash advances\",\u001b[0m\u001b[48;2;245;245;220m                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 5\u001b[0m\u001b[48;2;245;245;220m                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 0.05\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S. \u001b[0m\u001b[48;2;245;245;220m         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mdollars.\",\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 3\u001b[0m\u001b[48;2;245;245;220m                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"value\": 0.03\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -1076,21 +1438,28 @@
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"interest_rates\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        \"purchase\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"after_apr\": 19.49\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mthat your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mRate.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        \"my_chase_loan\": {\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 19.49\u001b[0m\u001b[48;2;245;245;220m                                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 19.49,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mthe Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        \"balance_transfer\": {\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"after_apr\": 19.49\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mis open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        \"cash_advance\": {\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 29.49\u001b[0m\u001b[48;2;245;245;220m                                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 29.49,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPrime Rate.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        \"penalty\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"maximum_apr\": 29.99\u001b[0m\u001b[48;2;245;245;220m                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"apr\": 29.99,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        }\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    }\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -1106,10 +1475,16 @@
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 2,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                incorrect_value='my chase plan',\u001b[0m\u001b[48;2;240;255;240m                                                       \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                fix_value='my chase',\u001b[0m\u001b[48;2;240;255;240m                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                fail_results=[\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                    FailResult(\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        outcome='fail',\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        metadata=None,\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        fix_value='my chase'\u001b[0m\u001b[48;2;240;255;240m                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                    )\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                ],\u001b[0m\u001b[48;2;240;255;240m                                                                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                path=['fees', 1, 'name']\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            ),\u001b[0m\u001b[48;2;240;255;240m                                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'My Chase Plan Fee (fixed finance charge)',\u001b[0m\u001b[48;2;240;255;240m                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1121,20 +1496,20 @@
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Balance Transfers Intro fee of either $5 or 3% of the amount of each \u001b[0m\u001b[48;2;240;255;240m      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtransfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m$5 or 5% of the amount of each transfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m                                       \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 3\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.05\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 4,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'cash advances',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 5\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.05\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'foreign transactions',\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Foreign Transactions 3% of the amount of each transaction in U.S. \u001b[0m\u001b[48;2;240;255;240m         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mdollars.',\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 3\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.03\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 6,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1144,10 +1519,16 @@
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 7,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                incorrect_value='over-the-credit-limit',\u001b[0m\u001b[48;2;240;255;240m                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                fix_value='over-the-credit-limit',\u001b[0m\u001b[48;2;240;255;240m                                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                fail_results=[\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                    FailResult(\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        outcome='fail',\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        metadata=None,\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                        fix_value='over-the-credit-limit'\u001b[0m\u001b[48;2;240;255;240m                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                    )\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                ],\u001b[0m\u001b[48;2;240;255;240m                                                                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m                path=['fees', 6, 'name']\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            ),\u001b[0m\u001b[48;2;240;255;240m                                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Over-the-Credit-Limit None',\u001b[0m\u001b[48;2;240;255;240m                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1167,16 +1548,36 @@
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        }\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    ],\u001b[0m\u001b[48;2;240;255;240m                                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    'interest_rates': {\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'purchase': {'apr': 0, 'after_apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'my_chase_loan': {'apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'balance_transfer': {'apr': 0, 'after_apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'cash_advance': {'apr': 29.49},\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'penalty': {'apr': 0, 'maximum_apr': 29.99}\u001b[0m\u001b[48;2;240;255;240m                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'purchase': {\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mthat your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mRate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'my_chase_loan': {\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 19.49,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'My Chase Loan SM APR 19.49%. This APR will vary with the market based on \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mthe Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                       \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'balance_transfer': {\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Balance Transfer APR 0% Intro APR for the first 18 months that your Account\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mis open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'cash_advance': {\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 29.49,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Cash Advance APR 29.49%. This APR will vary with the market based on the \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mPrime Rate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'penalty': {\u001b[0m\u001b[48;2;240;255;240m                                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 29.99,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        }\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    }\u001b[0m\u001b[48;2;240;255;240m                                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m                                                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "│   ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
-       "└── ╭────────────────────────────────────────────────── Step 2 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
        "    │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mI was given the following JSON response, which had problems due to incorrect values.\u001b[0m\u001b[48;2;240;248;255m                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1184,18 +1585,98 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m{\u001b[0m\u001b[48;2;240;248;255m                                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \"fees\": [\u001b[0m\u001b[48;2;240;248;255m                                                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 1,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"annual membership\",\u001b[0m\u001b[48;2;240;248;255m                                                                     \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Annual Membership Fee\",\u001b[0m\u001b[48;2;240;248;255m                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 2,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"incorrect_value\": \"my chase plan\",\u001b[0m\u001b[48;2;240;248;255m                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m                                                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      }\u001b[0m\u001b[48;2;240;248;255m                                                                                                \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m                                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m          \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        ]\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      },\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"My Chase Plan Fee (fixed finance charge)\",\u001b[0m\u001b[48;2;240;248;255m                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 1.72\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 3,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"balance transfers\",\u001b[0m\u001b[48;2;240;248;255m                                                                     \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer, \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mwhichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5% \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mof the amount of each transfer, whichever is greater.\",\u001b[0m\u001b[48;2;240;248;255m                                                \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0.05\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 4,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"cash advances\",\u001b[0m\u001b[48;2;240;248;255m                                                                         \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0.05\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 5,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;240;248;255m                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;240;248;255m     \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0.03\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 6,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"late payment\",\u001b[0m\u001b[48;2;240;248;255m                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Late Payment Up to $40.\",\u001b[0m\u001b[48;2;240;248;255m                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 7,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"incorrect_value\": \"over-the-credit-limit\",\u001b[0m\u001b[48;2;240;248;255m                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m                                                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      }\u001b[0m\u001b[48;2;240;248;255m                                                                                                \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m                                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m          \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        ]\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      },\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Over-the-Credit-Limit None\",\u001b[0m\u001b[48;2;240;248;255m                                                     \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 8,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"return payment\",\u001b[0m\u001b[48;2;240;248;255m                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Return Payment Up to $40.\",\u001b[0m\u001b[48;2;240;248;255m                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"index\": 9,\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"name\": \"return check\",\u001b[0m\u001b[48;2;240;248;255m                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Return Check None\",\u001b[0m\u001b[48;2;240;248;255m                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"value\": 0\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    }\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  ],\u001b[0m\u001b[48;2;240;248;255m                                                                                                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \"interest_rates\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \"purchase\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"apr\": 0,\u001b[0m\u001b[48;2;240;248;255m                                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months that \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255myour Account is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \"my_chase_loan\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"apr\": 19.49,\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on the \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mPrime Rate.\"\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \"balance_transfer\": {\u001b[0m\u001b[48;2;240;248;255m                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"apr\": 0,\u001b[0m\u001b[48;2;240;248;255m                                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account is \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mopen. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;240;248;255m                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \"cash_advance\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"apr\": 29.49,\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the Prime \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mRate.\"\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    },\u001b[0m\u001b[48;2;240;248;255m                                                                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \"penalty\": {\u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"apr\": 29.99,\u001b[0m\u001b[48;2;240;248;255m                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;240;248;255m       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    }\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  ]\u001b[0m\u001b[48;2;240;248;255m                                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  }\u001b[0m\u001b[48;2;240;248;255m                                                                                                    \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m}\u001b[0m\u001b[48;2;240;248;255m                                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHelp me correct the incorrect values based on the given error messages.\u001b[0m\u001b[48;2;240;248;255m                                \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1206,9 +1687,14 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m             \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                 \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                             \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1218,26 +1704,118 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mrequests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, \u001b[0m\u001b[48;2;240;248;255m   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255menter `null`.\u001b[0m\u001b[48;2;240;248;255m                                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;240;248;255m                         \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'foo': 'example one'}}`\u001b[0m\u001b[48;2;240;248;255m                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255metc.]}}`\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\u001b[0m\u001b[48;2;240;248;255m                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m                                                                                                       \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant only capable of communicating with valid JSON, and no other text.\u001b[0m\u001b[48;2;255;240;242m          \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m                                                                                                       \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m`name` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;255;240;242m      \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mrequests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, \u001b[0m\u001b[48;2;255;240;242m   \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242menter `null`.\u001b[0m\u001b[48;2;255;240;242m                                                                                          \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m                                                                                                       \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;255;240;242m                         \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'foo': 'example one'}`\u001b[0m\u001b[48;2;255;240;242m                    \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;255;240;242m   \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242metc.]}`\u001b[0m\u001b[48;2;255;240;242m                                                                                                \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;255;240;242m                       \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m                                                                                                       \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n",
+       "    │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235mNo message history.\u001b[0m\u001b[48;2;231;223;235m                                                                                    \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m                                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m  \"fees\": [\u001b[0m\u001b[48;2;245;245;220m                                                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"my-chase-plan\"\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 1,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"annual membership\",\u001b[0m\u001b[48;2;245;245;220m                                                                     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Annual Membership Fee\",\u001b[0m\u001b[48;2;245;245;220m                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"over-credit-limit\"\u001b[0m\u001b[48;2;245;245;220m                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 2,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"my chase plan\",\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"My Chase Plan Fee (fixed finance charge)\",\u001b[0m\u001b[48;2;245;245;220m                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0.0172\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"balance transfers\",\u001b[0m\u001b[48;2;245;245;220m                                                                     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Balance Transfers Intro fee of either $5 or 3% of the amount of each transfer, \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mwhichever is greater, on transfers made within 60 days of account opening. After that: Either $5 or 5% \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mof the amount of each transfer, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0.05\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"cash advances\",\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0.05\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;245;245;220m                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Foreign Transactions 3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;245;245;220m     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0.03\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"late payment\",\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Late Payment Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"over-the-credit-limit\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Over-the-Credit-Limit None\",\u001b[0m\u001b[48;2;245;245;220m                                                     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 8,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"return payment\",\u001b[0m\u001b[48;2;245;245;220m                                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Return Payment Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"index\": 9,\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"name\": \"return check\",\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Return Check None\",\u001b[0m\u001b[48;2;245;245;220m                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"value\": 0\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    }\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m  ]\u001b[0m\u001b[48;2;245;245;220m                                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m  ],\u001b[0m\u001b[48;2;245;245;220m                                                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m  \"interest_rates\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"purchase\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"apr\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months that \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220myour Account is open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"my_chase_loan\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"apr\": 19.49,\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"My Chase Loan SM APR 19.49%. This APR will vary with the market based on the \u001b[0m\u001b[48;2;245;245;220m    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPrime Rate.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"balance_transfer\": {\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"apr\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Balance Transfer APR 0% Intro APR for the first 18 months that your Account is \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mopen. After that, 19.49%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"cash_advance\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"apr\": 29.49,\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Cash Advance APR 29.49%. This APR will vary with the market based on the Prime \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mRate.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"penalty\": {\u001b[0m\u001b[48;2;245;245;220m                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"apr\": 29.99,\u001b[0m\u001b[48;2;245;245;220m                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"explanation\": \"Up to 29.99%. This APR will vary with the market based on the Prime Rate.\"\u001b[0m\u001b[48;2;245;245;220m       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    }\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m  }\u001b[0m\u001b[48;2;245;245;220m                                                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n",
@@ -1251,9 +1829,9 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 2,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'my-chase-plan',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'my chase',\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'My Chase Plan Fee (fixed finance charge)',\u001b[0m\u001b[48;2;240;255;240m                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 1.72\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.0172\u001b[0m\u001b[48;2;240;255;240m                                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 3,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1261,20 +1839,20 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Balance Transfers Intro fee of either $5 or 3% of the amount of each \u001b[0m\u001b[48;2;240;255;240m      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtransfer, whichever is greater, on transfers made within 60 days of account opening. After that: Either\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m$5 or 5% of the amount of each transfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m                                       \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 3\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.05\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 4,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'cash advances',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 5\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.05\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'foreign transactions',\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Foreign Transactions 3% of the amount of each transaction in U.S. \u001b[0m\u001b[48;2;240;255;240m         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mdollars.',\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 3\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0.03\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 6,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1284,7 +1862,7 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 7,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'over-credit-limit',\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'over-the-credit-limit',\u001b[0m\u001b[48;2;240;255;240m                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Over-the-Credit-Limit None',\u001b[0m\u001b[48;2;240;255;240m                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'value': 0\u001b[0m\u001b[48;2;240;255;240m                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1302,18 +1880,38 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        }\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    ],\u001b[0m\u001b[48;2;240;255;240m                                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    'interest_rates': {\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'purchase': {'apr': 0, 'after_apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'my_chase_loan': {'apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'balance_transfer': {'apr': 0, 'after_apr': 19.49},\u001b[0m\u001b[48;2;240;255;240m                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'cash_advance': {'apr': 29.49},\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'penalty': {'apr': 0, 'maximum_apr': 29.99}\u001b[0m\u001b[48;2;240;255;240m                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'purchase': {\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Purchase Annual Percentage Rate (APR) 0% Intro APR for the first 18 months \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mthat your Account is open. After that, 19.49%. This APR will vary with the market based on the Prime \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mRate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'my_chase_loan': {\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 19.49,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'My Chase Loan SM APR 19.49%. This APR will vary with the market based on \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mthe Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                       \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'balance_transfer': {\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Balance Transfer APR 0% Intro APR for the first 18 months that your Account\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mis open. After that, 19.49%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'cash_advance': {\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 29.49,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Cash Advance APR 29.49%. This APR will vary with the market based on the \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mPrime Rate.'\u001b[0m\u001b[48;2;240;255;240m                                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        'penalty': {\u001b[0m\u001b[48;2;240;255;240m                                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'apr': 29.99,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'explanation': 'Up to 29.99%. This APR will vary with the market based on the Prime Rate.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        }\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    }\u001b[0m\u001b[48;2;240;255;240m                                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m                                                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
       ]
      },
-     "execution_count": 9,
+     "execution_count": 17,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1339,7 +1937,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.16"
+   "version": "3.11.3"
   },
   "orig_nbformat": 4,
   "vscode": {
diff --git a/docs/examples/generate_structured_data.ipynb b/docs/examples/generate_structured_data.ipynb
index d0ad33b4d..b04475e47 100644
--- a/docs/examples/generate_structured_data.ipynb
+++ b/docs/examples/generate_structured_data.ipynb
@@ -29,12 +29,19 @@
     "\n",
     "## Step 1: Generating `RAIL` Spec\n",
     "\n",
-    "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string."
+    "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string.  We will also show the same RAIL spec in a code-first format using a Pydantic model."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "RAIL spec as an XML string:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -56,12 +63,54 @@
     "\n",
     "Generate a dataset of fake user orders. Each row of the dataset should be valid.\n",
     "\n",
-    "@complete_json_suffix\n",
+    "${gr.complete_json_suffix}\n",
     "\n",
     "\n",
     "\"\"\""
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Rail spec as a Pydantic model:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from pydantic import BaseModel, Field\n",
+    "from guardrails.validators import ValidLength, TwoWords, ValidRange\n",
+    "from datetime import date\n",
+    "from typing import List\n",
+    "\n",
+    "prompt = \"\"\"\n",
+    "Generate a dataset of fake user orders. Each row of the dataset should be valid.\n",
+    "\n",
+    "${gr.complete_json_suffix}\"\"\"\n",
+    "\n",
+    "class Order(BaseModel):\n",
+    "    user_id: str = Field(description=\"The user's id.\", validators=[(\"1-indexed\", \"noop\")])\n",
+    "    user_name: str = Field(\n",
+    "        description=\"The user's first name and last name\",\n",
+    "        validators=[TwoWords()]\n",
+    "    )\n",
+    "    num_orders: int = Field(\n",
+    "        description=\"The number of orders the user has placed\",\n",
+    "        validators=[ValidRange(0, 50)]\n",
+    "    )\n",
+    "    last_order_date: date = Field(description=\"Date of last order\")\n",
+    "\n",
+    "class Orders(BaseModel):\n",
+    "    user_orders: List[Order] = Field(\n",
+    "        description=\"Generate a list of user, and how many orders they have placed in the past.\",\n",
+    "        validators=[ValidLength(10, 10, on_fail=\"noop\")]\n",
+    "    )"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -77,26 +126,47 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 19,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator 1-indexed is not valid for element string.\n",
-      "  warnings.warn(\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "import guardrails as gd\n",
     "\n",
-    "from rich import print\n",
-    "\n",
+    "from rich import print"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "From our RAIL string:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [],
+   "source": [
     "guard = gd.Guard.from_rail_string(rail_str)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "From our Pydantic model:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "guard = gd.Guard.from_pydantic(output_class=Orders, prompt=prompt)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -106,7 +176,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 22,
    "metadata": {},
    "outputs": [
     {
@@ -119,13 +189,13 @@
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "<output>\n",
-       "    <list name=\"user_orders\" description=\"Generate a list of user, and how many orders they have placed in the \n",
-       "past.\" format=\"length: min=10 max=10\">\n",
+       "    <list name=\"user_orders\" format=\"length: min=10 max=10\" description=\"Generate a list of user, and how many \n",
+       "orders they have placed in the past.\">\n",
        "        <object>\n",
-       "            <string name=\"user_id\" description=\"The user's id.\" format=\"1-indexed\"/>\n",
-       "            <string name=\"user_name\" description=\"The user's first name and last name\" format=\"two-words\"/>\n",
-       "            <integer name=\"num_orders\" description=\"The number of orders the user has placed\" format=\"valid-range: \n",
-       "0 50\"/>\n",
+       "            <string name=\"user_id\" format=\"1-indexed\" description=\"The user's id.\"/>\n",
+       "            <string name=\"user_name\" format=\"two-words\" description=\"The user's first name and last name\"/>\n",
+       "            <integer name=\"num_orders\" format=\"valid-range: min=0 max=50\" description=\"The number of orders the \n",
+       "user has placed\"/>\n",
        "            <date name=\"last_order_date\" description=\"Date of last order\"/>\n",
        "        </object>\n",
        "    </list>\n",
@@ -135,15 +205,14 @@
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n"
       ],
       "text/plain": [
@@ -154,13 +223,13 @@
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n",
-       "\u001b[39m    \u001b[0m\n",
+       "\u001b[39m    \u001b[0m\n",
        "\u001b[39m        \u001b[0m\n",
-       "\u001b[39m            \u001b[0m\n",
-       "\u001b[39m            \u001b[0m\n",
-       "\u001b[39m            \u001b[0m\n",
+       "\u001b[39m            \u001b[0m\n",
+       "\u001b[39m            \u001b[0m\n",
+       "\u001b[39m            \u001b[0m\n",
        "\u001b[39m            \u001b[0m\n",
        "\u001b[39m        <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>\u001b[0m\n",
        "\u001b[39m    <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>\u001b[0m\n",
@@ -170,15 +239,14 @@
        "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n",
        "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n",
        "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n",
-       "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n",
+       "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n",
        "\n",
        "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n",
-       "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n",
-       "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n",
+       "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n",
+       "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n",
        "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n",
-       "\n",
-       "JSON Object:\n"
+       "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n",
+       "\n"
       ]
      },
      "metadata": {},
@@ -198,15 +266,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [
     {
      "name": "stderr",
      "output_type": "stream",
      "text": [
-      "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar.  (This may have returned Python scalars in past versions.\n",
-      "  if isinstance(o, (numpy.bool, numpy.bool_)):\n"
+      "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n"
      ]
     }
    ],
@@ -230,7 +297,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 24,
    "metadata": {},
    "outputs": [
     {
@@ -239,15 +306,15 @@
        "
{\n",
        "    'user_orders': [\n",
        "        {'user_id': 1, 'user_name': 'John Smith', 'num_orders': 10, 'last_order_date': '2020-01-01'},\n",
-       "        {'user_id': 2, 'user_name': 'Jane Doe', 'num_orders': 5, 'last_order_date': '2020-02-01'},\n",
-       "        {'user_id': 3, 'user_name': 'Bob Jones', 'num_orders': 25, 'last_order_date': '2020-03-01'},\n",
-       "        {'user_id': 4, 'user_name': 'Alice Johnson', 'num_orders': 0, 'last_order_date': '2020-04-01'},\n",
-       "        {'user_id': 5, 'user_name': 'John Doe', 'num_orders': 15, 'last_order_date': '2020-05-01'},\n",
-       "        {'user_id': 6, 'user_name': 'Jane Smith', 'num_orders': 20, 'last_order_date': '2020-06-01'},\n",
-       "        {'user_id': 7, 'user_name': 'Bob Johnson', 'num_orders': 10, 'last_order_date': '2020-07-01'},\n",
-       "        {'user_id': 8, 'user_name': 'Alice Jones', 'num_orders': 30, 'last_order_date': '2020-08-01'},\n",
-       "        {'user_id': 9, 'user_name': 'John Johnson', 'num_orders': 40, 'last_order_date': '2020-09-01'},\n",
-       "        {'user_id': 10, 'user_name': 'Jane Jones', 'num_orders': 35, 'last_order_date': '2020-10-01'}\n",
+       "        {'user_id': 2, 'user_name': 'Jane Doe', 'num_orders': 20, 'last_order_date': '2020-02-01'},\n",
+       "        {'user_id': 3, 'user_name': 'Bob Jones', 'num_orders': 30, 'last_order_date': '2020-03-01'},\n",
+       "        {'user_id': 4, 'user_name': 'Alice Smith', 'num_orders': 40, 'last_order_date': '2020-04-01'},\n",
+       "        {'user_id': 5, 'user_name': 'John Doe', 'num_orders': 50, 'last_order_date': '2020-05-01'},\n",
+       "        {'user_id': 6, 'user_name': 'Jane Jones', 'num_orders': 0, 'last_order_date': '2020-06-01'},\n",
+       "        {'user_id': 7, 'user_name': 'Bob Smith', 'num_orders': 10, 'last_order_date': '2020-07-01'},\n",
+       "        {'user_id': 8, 'user_name': 'Alice Doe', 'num_orders': 20, 'last_order_date': '2020-08-01'},\n",
+       "        {'user_id': 9, 'user_name': 'John Jones', 'num_orders': 30, 'last_order_date': '2020-09-01'},\n",
+       "        {'user_id': 10, 'user_name': 'Jane Smith', 'num_orders': 40, 'last_order_date': '2020-10-01'}\n",
        "    ]\n",
        "}\n",
        "
\n" @@ -256,15 +323,15 @@ "\u001b[1m{\u001b[0m\n", " \u001b[32m'user_orders'\u001b[0m: \u001b[1m[\u001b[0m\n", " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'John Smith'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-01-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m2\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Doe'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m5\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-02-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m3\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Bob Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m25\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-03-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m4\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Alice Johnson'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m0\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-04-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m5\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'John Doe'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m15\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-05-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Smith'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m20\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-06-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Bob Johnson'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-07-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Alice Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m30\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-08-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'John Johnson'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m40\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-09-01'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m35\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-10-01'\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m2\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Doe'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m20\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-02-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m3\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Bob Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m30\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-03-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m4\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Alice Smith'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m40\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-04-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m5\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'John Doe'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m50\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-05-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m0\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-06-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Bob Smith'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-07-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Alice Doe'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m20\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-08-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'John Jones'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m30\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-09-01'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'user_id'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'user_name'\u001b[0m: \u001b[32m'Jane Smith'\u001b[0m, \u001b[32m'num_orders'\u001b[0m: \u001b[1;36m40\u001b[0m, \u001b[32m'last_order_date'\u001b[0m: \u001b[32m'2020-10-01'\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -279,14 +346,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ Generate a dataset of fake user orders. Each row of the dataset should be valid.                        │ │\n",
@@ -296,14 +363,14 @@
        "    │ │ it into.                                                                                                │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ <output>                                                                                                │ │\n",
-       "    │ │     <list name=\"user_orders\" description=\"Generate a list of user, and how many orders they have placed │ │\n",
-       "    │ │ in the past.\" format=\"length: min=10 max=10\">                                                           │ │\n",
+       "    │ │     <list name=\"user_orders\" format=\"length: min=10 max=10\" description=\"Generate a list of user, and   │ │\n",
+       "    │ │ how many orders they have placed in the past.\">                                                         │ │\n",
        "    │ │         <object>                                                                                        │ │\n",
-       "    │ │             <string name=\"user_id\" description=\"The user's id.\" format=\"1-indexed\"/>                    │ │\n",
-       "    │ │             <string name=\"user_name\" description=\"The user's first name and last name\"                  │ │\n",
-       "    │ │ format=\"two-words\"/>                                                                                    │ │\n",
-       "    │ │             <integer name=\"num_orders\" description=\"The number of orders the user has placed\"           │ │\n",
-       "    │ │ format=\"valid-range: 0 50\"/>                                                                            │ │\n",
+       "    │ │             <string name=\"user_id\" format=\"1-indexed\" description=\"The user's id.\"/>                    │ │\n",
+       "    │ │             <string name=\"user_name\" format=\"two-words\" description=\"The user's first name and last     │ │\n",
+       "    │ │ name\"/>                                                                                                 │ │\n",
+       "    │ │             <integer name=\"num_orders\" format=\"valid-range: min=0 max=50\" description=\"The number of    │ │\n",
+       "    │ │ orders the user has placed\"/>                                                                           │ │\n",
        "    │ │             <date name=\"last_order_date\" description=\"Date of last order\"/>                             │ │\n",
        "    │ │         </object>                                                                                       │ │\n",
        "    │ │     </list>                                                                                             │ │\n",
@@ -323,10 +390,18 @@
        "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ JSON Object:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ Json Output:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "    │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "    │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
-       "    │ │                                                                                                         │ │\n",
        "    │ │ {                                                                                                       │ │\n",
        "    │ │     \"user_orders\": [                                                                                    │ │\n",
        "    │ │         {                                                                                               │ │\n",
@@ -338,55 +413,55 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 2,                                                                               │ │\n",
        "    │ │             \"user_name\": \"Jane Doe\",                                                                    │ │\n",
-       "    │ │             \"num_orders\": 5,                                                                            │ │\n",
+       "    │ │             \"num_orders\": 20,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-02-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 3,                                                                               │ │\n",
        "    │ │             \"user_name\": \"Bob Jones\",                                                                   │ │\n",
-       "    │ │             \"num_orders\": 25,                                                                           │ │\n",
+       "    │ │             \"num_orders\": 30,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-03-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 4,                                                                               │ │\n",
-       "    │ │             \"user_name\": \"Alice Johnson\",                                                               │ │\n",
-       "    │ │             \"num_orders\": 0,                                                                            │ │\n",
+       "    │ │             \"user_name\": \"Alice Smith\",                                                                 │ │\n",
+       "    │ │             \"num_orders\": 40,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-04-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 5,                                                                               │ │\n",
        "    │ │             \"user_name\": \"John Doe\",                                                                    │ │\n",
-       "    │ │             \"num_orders\": 15,                                                                           │ │\n",
+       "    │ │             \"num_orders\": 50,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-05-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 6,                                                                               │ │\n",
-       "    │ │             \"user_name\": \"Jane Smith\",                                                                  │ │\n",
-       "    │ │             \"num_orders\": 20,                                                                           │ │\n",
+       "    │ │             \"user_name\": \"Jane Jones\",                                                                  │ │\n",
+       "    │ │             \"num_orders\": 0,                                                                            │ │\n",
        "    │ │             \"last_order_date\": \"2020-06-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 7,                                                                               │ │\n",
-       "    │ │             \"user_name\": \"Bob Johnson\",                                                                 │ │\n",
+       "    │ │             \"user_name\": \"Bob Smith\",                                                                   │ │\n",
        "    │ │             \"num_orders\": 10,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-07-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 8,                                                                               │ │\n",
-       "    │ │             \"user_name\": \"Alice Jones\",                                                                 │ │\n",
-       "    │ │             \"num_orders\": 30,                                                                           │ │\n",
+       "    │ │             \"user_name\": \"Alice Doe\",                                                                   │ │\n",
+       "    │ │             \"num_orders\": 20,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-08-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 9,                                                                               │ │\n",
-       "    │ │             \"user_name\": \"John Johnson\",                                                                │ │\n",
-       "    │ │             \"num_orders\": 40,                                                                           │ │\n",
+       "    │ │             \"user_name\": \"John Jones\",                                                                  │ │\n",
+       "    │ │             \"num_orders\": 30,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-09-01\"                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"user_id\": 10,                                                                              │ │\n",
-       "    │ │             \"user_name\": \"Jane Jones\",                                                                  │ │\n",
-       "    │ │             \"num_orders\": 35,                                                                           │ │\n",
+       "    │ │             \"user_name\": \"Jane Smith\",                                                                  │ │\n",
+       "    │ │             \"num_orders\": 40,                                                                           │ │\n",
        "    │ │             \"last_order_date\": \"2020-10-01\"                                                             │ │\n",
        "    │ │         }                                                                                               │ │\n",
        "    │ │     ]                                                                                                   │ │\n",
@@ -404,55 +479,55 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 2,                                                                               │ │\n",
        "    │ │             'user_name': 'Jane Doe',                                                                    │ │\n",
-       "    │ │             'num_orders': 5,                                                                            │ │\n",
+       "    │ │             'num_orders': 20,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-02-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 3,                                                                               │ │\n",
        "    │ │             'user_name': 'Bob Jones',                                                                   │ │\n",
-       "    │ │             'num_orders': 25,                                                                           │ │\n",
+       "    │ │             'num_orders': 30,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-03-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 4,                                                                               │ │\n",
-       "    │ │             'user_name': 'Alice Johnson',                                                               │ │\n",
-       "    │ │             'num_orders': 0,                                                                            │ │\n",
+       "    │ │             'user_name': 'Alice Smith',                                                                 │ │\n",
+       "    │ │             'num_orders': 40,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-04-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 5,                                                                               │ │\n",
        "    │ │             'user_name': 'John Doe',                                                                    │ │\n",
-       "    │ │             'num_orders': 15,                                                                           │ │\n",
+       "    │ │             'num_orders': 50,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-05-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 6,                                                                               │ │\n",
-       "    │ │             'user_name': 'Jane Smith',                                                                  │ │\n",
-       "    │ │             'num_orders': 20,                                                                           │ │\n",
+       "    │ │             'user_name': 'Jane Jones',                                                                  │ │\n",
+       "    │ │             'num_orders': 0,                                                                            │ │\n",
        "    │ │             'last_order_date': '2020-06-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 7,                                                                               │ │\n",
-       "    │ │             'user_name': 'Bob Johnson',                                                                 │ │\n",
+       "    │ │             'user_name': 'Bob Smith',                                                                   │ │\n",
        "    │ │             'num_orders': 10,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-07-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 8,                                                                               │ │\n",
-       "    │ │             'user_name': 'Alice Jones',                                                                 │ │\n",
-       "    │ │             'num_orders': 30,                                                                           │ │\n",
+       "    │ │             'user_name': 'Alice Doe',                                                                   │ │\n",
+       "    │ │             'num_orders': 20,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-08-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 9,                                                                               │ │\n",
-       "    │ │             'user_name': 'John Johnson',                                                                │ │\n",
-       "    │ │             'num_orders': 40,                                                                           │ │\n",
+       "    │ │             'user_name': 'John Jones',                                                                  │ │\n",
+       "    │ │             'num_orders': 30,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-09-01'                                                             │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'user_id': 10,                                                                              │ │\n",
-       "    │ │             'user_name': 'Jane Jones',                                                                  │ │\n",
-       "    │ │             'num_orders': 35,                                                                           │ │\n",
+       "    │ │             'user_name': 'Jane Smith',                                                                  │ │\n",
+       "    │ │             'num_orders': 40,                                                                           │ │\n",
        "    │ │             'last_order_date': '2020-10-01'                                                             │ │\n",
        "    │ │         }                                                                                               │ │\n",
        "    │ │     ]                                                                                                   │ │\n",
@@ -463,7 +538,7 @@
       ],
       "text/plain": [
        "Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mGenerate a dataset of fake user orders. Each row of the dataset should be valid.\u001b[0m\u001b[48;2;240;248;255m                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -473,14 +548,14 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \u001b[0m\u001b[48;2;240;248;255m                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                                                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                   \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                                                                \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m            \u001b[0m\u001b[48;2;240;248;255m                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        \u001b[0m\u001b[48;2;240;248;255m                                                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -500,10 +575,18 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m                                                                                                       \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"user_orders\": [\u001b[0m\u001b[48;2;245;245;220m                                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -515,55 +598,55 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 2,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Jane Doe\",\u001b[0m\u001b[48;2;245;245;220m                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 20,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-02-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 3,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Bob Jones\",\u001b[0m\u001b[48;2;245;245;220m                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 25,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 30,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-03-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 4,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Alice Johnson\",\u001b[0m\u001b[48;2;245;245;220m                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Alice Smith\",\u001b[0m\u001b[48;2;245;245;220m                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 40,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-04-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"John Doe\",\u001b[0m\u001b[48;2;245;245;220m                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 15,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 50,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-05-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Jane Smith\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 20,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Jane Jones\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-06-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 7,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Bob Johnson\",\u001b[0m\u001b[48;2;245;245;220m                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Bob Smith\",\u001b[0m\u001b[48;2;245;245;220m                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 10,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-07-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 8,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Alice Jones\",\u001b[0m\u001b[48;2;245;245;220m                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 30,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Alice Doe\",\u001b[0m\u001b[48;2;245;245;220m                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 20,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-08-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 9,\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"John Johnson\",\u001b[0m\u001b[48;2;245;245;220m                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 40,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"John Jones\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 30,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-09-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_id\": 10,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Jane Jones\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 35,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"user_name\": \"Jane Smith\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"num_orders\": 40,\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"last_order_date\": \"2020-10-01\"\u001b[0m\u001b[48;2;245;245;220m                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        }\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    ]\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -581,55 +664,55 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 2,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Jane Doe',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 20,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-02-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 3,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Bob Jones',\u001b[0m\u001b[48;2;240;255;240m                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 25,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 30,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-03-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 4,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Alice Johnson',\u001b[0m\u001b[48;2;240;255;240m                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Alice Smith',\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 40,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-04-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'John Doe',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 15,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 50,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-05-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 6,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Jane Smith',\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 20,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Jane Jones',\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-06-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 7,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Bob Johnson',\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Bob Smith',\u001b[0m\u001b[48;2;240;255;240m                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 10,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-07-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 8,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Alice Jones',\u001b[0m\u001b[48;2;240;255;240m                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 30,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Alice Doe',\u001b[0m\u001b[48;2;240;255;240m                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 20,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-08-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 9,\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'John Johnson',\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 40,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'John Jones',\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 30,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-09-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_id': 10,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Jane Jones',\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 35,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'user_name': 'Jane Smith',\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'num_orders': 40,\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'last_order_date': '2020-10-01'\u001b[0m\u001b[48;2;240;255;240m                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        }\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    ]\u001b[0m\u001b[48;2;240;255;240m                                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -663,7 +746,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.16"
+   "version": "3.11.3"
   },
   "orig_nbformat": 4
  },
diff --git a/docs/examples/guardrails_with_chat_models.ipynb b/docs/examples/guardrails_with_chat_models.ipynb
index 99da42007..1a03e9bfd 100644
--- a/docs/examples/guardrails_with_chat_models.ipynb
+++ b/docs/examples/guardrails_with_chat_models.ipynb
@@ -25,7 +25,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 36,
    "metadata": {},
    "outputs": [
     {
@@ -88,7 +88,7 @@
     "    \n",
     "    You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n",
     "\n",
-    "    @json_suffix_prompt_examples\n",
+    "    ${gr.json_suffix_prompt_examples}\n",
     "    \n",
     "\n",
     "\n",
@@ -96,33 +96,63 @@
     "    Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n",
     "    `null`.\n",
     "\n",
-    "    {{document}}\n",
+    "    ${document}\n",
     "\n",
     "    Extract information from this document and return a JSON that follows the correct schema.\n",
     "\n",
-    "    @xml_prefix_prompt\n",
+    "    ${gr.xml_prefix_prompt}\n",
     "\n",
-    "    {output_schema}\n",
+    "    ${output_schema}\n",
     "    \n",
     "    ```\n",
     "\n",
+    "=== \"Pydantic with instructions\"\n",
+    "    ```py\n",
+    "    instructions = \"\"\"You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n",
+    "\n",
+    "    ${gr.json_suffix_prompt_examples}\"\"\"\n",
+    "\n",
+    "    prompt = \"\"\"Given the following document, answer the following questions. If the answer doesn't exist in the document, enter `null`.\n",
+    "\n",
+    "    ${document}\n",
+    "\n",
+    "    Extract information from this document and return a JSON that follows the correct schema.\n",
+    "\n",
+    "    ${gr.xml_prefix_prompt}\n",
+    "\n",
+    "    ${output_schema}\"\"\"\n",
+    "    ```\n",
+    "\n",
     "=== \"RAIL Spec without instruction tag\"\n",
     "    \n",
     "    ```xml\n",
     "    \n",
     "    Given the following document, answer the following questions. If the answer doesn't exist in the document, enter `null`.\n",
     "\n",
-    "    {{document}}\n",
+    "    ${document}\n",
     "\n",
-    "    @xml_prefix_prompt\n",
+    "    ${gr.xml_prefix_prompt}\n",
     "\n",
-    "    {output_schema}\n",
+    "    ${output_schema}\n",
     "\n",
-    "    @json_suffix_prompt_v2_wo_none\n",
+    "    ${gr.json_suffix_prompt_v2_wo_none}\n",
     "    \n",
     "    ```\n",
     "\n",
-    "After materialization, the two specs will look like this:\n",
+    "=== \"Pydantic without instructions\"\n",
+    "    ```py\n",
+    "    prompt = \"\"\"Given the following document, answer the following questions. If the answer doesn't exist in the document, enter `null`.\n",
+    "\n",
+    "    ${document}\n",
+    "\n",
+    "    ${gr.xml_prefix_prompt}\n",
+    "\n",
+    "    ${output_schema}\n",
+    "\n",
+    "    ${gr.json_suffix_prompt_v2_wo_none}\"\"\"\n",
+    "    ```\n",
+    "\n",
+    "After materialization, the two variations of the RAIL specification will look like this when passed to the LLM (regarless if they were initialized from xml or a Pydantic model):\n",
     "\n",
     "=== \"RAIL Spec with instruction tag\"\n",
     "\n",
@@ -143,13 +173,13 @@
     "    Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n",
     "    `null`.\n",
     "\n",
-    "    {{document}}\n",
+    "    ${document}\n",
     "\n",
     "    Extract information from this document and return a JSON that follows the correct schema.\n",
     "\n",
     "    Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
     "\n",
-    "    {output_schema}\n",
+    "    ${output_schema}\n",
     "    \n",
     "    ```\n",
     "\n",
@@ -159,11 +189,11 @@
     "    \n",
     "    Given the following document, answer the following questions. If the answer doesn't exist in the document, enter `null`.\n",
     "\n",
-    "    {{document}}\n",
+    "    ${document}\n",
     "\n",
     "    Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
     "\n",
-    "    {output_schema}\n",
+    "    ${output_schema}\n",
     "\n",
     "    ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
     "\n",
@@ -174,12 +204,12 @@
     "    \n",
     "    ```\n",
     "\n",
-    "Here's the final RAIL spec:"
+    "Here's the final RAIL spec as XML:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 37,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -196,14 +226,14 @@
     "            \n",
     "        
\n", "
\n", - " \n", + " \n", "\n", "\n", "\n", "\n", "You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n", "\n", - "@json_suffix_prompt_examples\n", + "${gr.json_suffix_prompt_examples}\n", "\n", "\n", "\n", @@ -211,19 +241,62 @@ "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", "`null`.\n", "\n", - "{{document}}\n", + "${document}\n", "\n", "Extract information from this document and return a JSON that follows the correct schema.\n", "\n", - "@xml_prefix_prompt\n", + "${gr.xml_prefix_prompt}\n", "\n", - "{output_schema}\n", + "${output_schema}\n", "\n", "\n", "\n", "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or using a Pydantic model for the output schema:" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "from guardrails.validators import LowerCase, TwoWords, OneLine\n", + "from pydantic import BaseModel, Field\n", + "from typing import List, Optional\n", + "\n", + "instructions = \"\"\"You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n", + "\n", + "${gr.json_suffix_prompt_examples}\"\"\"\n", + "\n", + "prompt = \"\"\"Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", + "`null`.\n", + "\n", + "${document}\n", + "\n", + "Extract information from this document and return a JSON that follows the correct schema.\n", + "\n", + "${gr.xml_prefix_prompt}\n", + "\n", + "${output_schema}\"\"\"\n", + "\n", + "class Fee(BaseModel):\n", + " index: int = Field(validators=[(\"1-indexed\", \"noop\")])\n", + " name: str = Field(validators=[LowerCase(on_fail=\"fix\"), TwoWords(on_fail=\"reask\")])\n", + " explanation: str = Field(validators=[OneLine()])\n", + " value: float = Field(validators=[(\"percentage\", \"noop\")])\n", + "\n", + "class CreditCardAgreement(BaseModel):\n", + " fees: List[Fee] = Field(description=\"What fees and charges are associated with my account?\")\n", + " interest_rates: Optional[dict] = Field(description=\"What are the interest rates offered by the bank on savings and checking accounts, loans, and credit products?\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -237,18 +310,25 @@ "3. Compiles the schema and type info from the RAIL spec and adds it to the prompt." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Creating the guard from XML:" + ] + }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 42, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/guardrails/guardrails/schema.py:197: UserWarning: Validator 1-indexed is not valid for element integer.\n", + "/Users/calebcourier/Projects/shreyar/guardrails/guardrails/schema.py:218: UserWarning: Validator 1-indexed is not valid for element integer.\n", " warnings.warn(\n", - "/Users/shreyarajpal/guardrails/guardrails/schema.py:197: UserWarning: Validator percentage is not valid for element float.\n", + "/Users/calebcourier/Projects/shreyar/guardrails/guardrails/schema.py:218: UserWarning: Validator percentage is not valid for element float.\n", " warnings.warn(\n" ] } @@ -257,6 +337,22 @@ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from the Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement, instructions=instructions, prompt=prompt)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -268,17 +364,16 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n",
-       "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n",
+       "
Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n",
        "`null`.\n",
        "\n",
-       "{document}\n",
+       "${document}\n",
        "\n",
        "Extract information from this document and return a JSON that follows the correct schema.\n",
        "\n",
@@ -299,15 +394,13 @@
        "checking accounts, loans, and credit products?\"/>\n",
        "</output>\n",
        "\n",
-       "\n",
        "
\n" ], "text/plain": [ - "\n", "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", "`null`.\n", "\n", - "\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", "\n", "Extract information from this document and return a JSON that follows the correct schema.\n", "\n", @@ -327,7 +420,6 @@ "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[1m>\u001b[0m\n", - "\n", "\n" ] }, @@ -349,46 +441,42 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\n",
-       "You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n",
+       "
You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n",
        "\n",
        "\n",
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
-       "\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
        "
\n" ], "text/plain": [ - "\n", "You are a helpful assistant only capable of communicating with valid JSON, and no other text.\n", "\n", "\n", "ONLY return a valid JSON object \u001b[1m(\u001b[0mno other text is necessary\u001b[1m)\u001b[0m, where the key of the field in JSON is the `name` \n", "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n", "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n", - "specific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[3;35mNone\u001b[0m`.\n", + "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n", "\n", "Here are examples of simple \u001b[1m(\u001b[0mXML, JSON\u001b[1m)\u001b[0m pairs that show the expected behavior:\n", - "- `\u001b[1m<\u001b[0m\u001b[1;95mstring\u001b[0m\u001b[39m \u001b[0m\u001b[33mname\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m \u001b[0m\u001b[33mformat\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'two-words lower-case'\u001b[0m\u001b[39m \u001b[0m\u001b[35m/\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "- `\u001b[1m<\u001b[0m\u001b[1;95mstring\u001b[0m\u001b[39m \u001b[0m\u001b[33mname\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m \u001b[0m\u001b[33mformat\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'two-words lower-case'\u001b[0m\u001b[39m \u001b[0m\u001b[35m/\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", - "\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n" ] }, @@ -414,18 +502,9 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 46, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" - ] - } - ], + "outputs": [], "source": [ "import openai\n", "\n", @@ -449,165 +528,105 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
{\n",
-       "    'fees': [\n",
-       "        {'index': 1, 'name': 'annual membership', 'explanation': 'None', 'value': 0.0},\n",
-       "        {\n",
-       "            'index': 2,\n",
-       "            'name': 'My Chase',\n",
-       "            'explanation': 'monthly fee of 0% of the amount of each eligible purchase transaction or amount \n",
-       "selected to create a my chase plan while in the 0% intro purchase apr period. after that, monthly fee of 1.72% of \n",
-       "the amount of each eligible purchase transaction or amount selected to create a my chase plan. the my chase plan \n",
-       "fee will be determined at the time each my chase plan is created and will remain the same until the my chase plan \n",
+       "
SkeletonReAsk(\n",
+       "    incorrect_value={\n",
+       "        'fees': [\n",
+       "            {'index': 1, 'name': 'annual membership fee', 'explanation': 'None', 'value': 0.0},\n",
+       "            {\n",
+       "                'index': 2,\n",
+       "                'name': 'my chase plan fee',\n",
+       "                'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction or amount \n",
+       "selected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that, monthly fee of 1.72% of \n",
+       "the amount of each eligible purchase transaction or amount selected to create a My Chase Plan. The My Chase Plan \n",
+       "Fee will be determined at the time each My Chase Plan is created and will remain the same until the My Chase Plan \n",
        "is paid in full.',\n",
-       "            'value': 1.72\n",
-       "        },\n",
-       "        {\n",
-       "            'index': 3,\n",
-       "            'name': 'balance transfers',\n",
-       "            'explanation': 'intro fee of either $5 or 3% of the amount of each transfer, whichever is greater, on \n",
-       "transfers made within 60 days of account opening. after that: either $5 or 5% of the amount of each transfer, \n",
-       "whichever is greater.',\n",
-       "            'value': 5.0\n",
-       "        },\n",
-       "        {\n",
-       "            'index': 4,\n",
-       "            'name': 'cash advances',\n",
-       "            'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\n",
-       "            'value': 5.0\n",
-       "        },\n",
-       "        {\n",
-       "            'index': 5,\n",
-       "            'name': 'foreign transactions',\n",
-       "            'explanation': '3% of the amount of each transaction in U.S. dollars.',\n",
-       "            'value': 3.0\n",
-       "        },\n",
-       "        {'index': 6, 'name': 'Late Payment', 'explanation': 'Up to $40.', 'value': None},\n",
-       "        {'index': 7, 'name': 'over-the-credit-limit penalty', 'explanation': 'None', 'value': None},\n",
-       "        {'index': 8, 'name': 'Return Payment', 'explanation': 'Up to $40.', 'value': None},\n",
-       "        {'index': 9, 'name': 'Return Check', 'explanation': 'None', 'value': None}\n",
-       "    ],\n",
-       "    'interest_rates': {\n",
-       "        'purchase annual percentage rate (apr)': {\n",
-       "            'introductory rate': 0.0,\n",
-       "            'introductory period': 'first 18 months',\n",
-       "            'standard rate': 19.49,\n",
-       "            'variable': True,\n",
-       "            'description': 'The interest rate charged on purchases made with the credit card.'\n",
-       "        },\n",
-       "        'my chase loan sm apr': {\n",
-       "            'rate': 19.49,\n",
-       "            'variable': True,\n",
-       "            'description': 'The interest rate charged on My Chase Loan balances.'\n",
-       "        },\n",
-       "        'balance transfer apr': {\n",
-       "            'introductory rate': 0.0,\n",
-       "            'introductory period': 'first 18 months',\n",
-       "            'standard rate': 19.49,\n",
-       "            'variable': True,\n",
-       "            'description': 'The interest rate charged on balances transferred to the credit card.'\n",
-       "        },\n",
-       "        'cash advance apr': {\n",
-       "            'rate': 29.49,\n",
-       "            'variable': True,\n",
-       "            'description': 'The interest rate charged on cash advances taken with the credit card.'\n",
-       "        },\n",
-       "        'penalty apr and when it applies': {\n",
-       "            'rate': 'Up to 29.99%',\n",
-       "            'variable': True,\n",
-       "            'description': 'The interest rate charged on the credit card when a penalty is applied.'\n",
-       "        },\n",
-       "        'maximum apr': {\n",
-       "            'rate': 29.99,\n",
-       "            'variable': True,\n",
-       "            'description': 'The highest interest rate that can be charged on the credit card.'\n",
-       "        }\n",
-       "    }\n",
-       "}\n",
+       "                'value': 1.72\n",
+       "            },\n",
+       "            {\n",
+       "                'index': 3,\n",
+       "                'name': 'balance transfers intro fee',\n",
+       "                'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater, on transfers \n",
+       "made within 60 days of account opening. After that: Either $5 or 5% of the amount of each transfer, whichever is \n",
+       "greater.',\n",
+       "                'value': 5.0\n",
+       "            },\n",
+       "            {\n",
+       "                'index': 4,\n",
+       "                'name': 'cash advances',\n",
+       "                'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\n",
+       "                'value': 5.0\n",
+       "            },\n",
+       "            {\n",
+       "                'index': 5,\n",
+       "                'name': 'foreign transactions',\n",
+       "                'explanation': '3% of the amount of each transaction in U.S. dollars.',\n",
+       "                'value': 3.0\n",
+       "            },\n",
+       "            {'index': 6, 'name': 'late payment', 'explanation': 'Up to $40.', 'value': 40.0},\n",
+       "            {'index': 7, 'name': 'over-the-credit-limit', 'explanation': 'None', 'value': None},\n",
+       "            {'index': 8, 'name': 'return payment', 'explanation': 'Up to $40.', 'value': 40.0},\n",
+       "            {'index': 9, 'name': 'return check', 'explanation': 'None', 'value': None}\n",
+       "        ],\n",
+       "        'interest_rates': None\n",
+       "    },\n",
+       "    fail_results=[\n",
+       "        FailResult(outcome='fail', metadata=None, error_message='JSON does not match schema', fix_value=None)\n",
+       "    ]\n",
+       ")\n",
        "
\n" ], "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[32m'fees'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'annual membership'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'My Chase'\u001b[0m,\n", - " \u001b[32m'explanation'\u001b[0m: \u001b[32m'monthly fee of 0% of the amount of each eligible purchase transaction or amount \u001b[0m\n", - "\u001b[32mselected to create a my chase plan while in the 0% intro purchase apr period. after that, monthly fee of 1.72% of \u001b[0m\n", - "\u001b[32mthe amount of each eligible purchase transaction or amount selected to create a my chase plan. the my chase plan \u001b[0m\n", - "\u001b[32mfee will be determined at the time each my chase plan is created and will remain the same until the my chase plan \u001b[0m\n", + "\u001b[1;35mSkeletonReAsk\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mincorrect_value\u001b[0m=\u001b[1m{\u001b[0m\n", + " \u001b[32m'fees'\u001b[0m: \u001b[1m[\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'annual membership fee'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'my chase plan fee'\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Monthly fee of 0% of the amount of each eligible purchase transaction or amount \u001b[0m\n", + "\u001b[32mselected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that, monthly fee of 1.72% of \u001b[0m\n", + "\u001b[32mthe amount of each eligible purchase transaction or amount selected to create a My Chase Plan. The My Chase Plan \u001b[0m\n", + "\u001b[32mFee will be determined at the time each My Chase Plan is created and will remain the same until the My Chase Plan \u001b[0m\n", "\u001b[32mis paid in full.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m1.72\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'balance transfers'\u001b[0m,\n", - " \u001b[32m'explanation'\u001b[0m: \u001b[32m'intro fee of either $5 or 3% of the amount of each transfer, whichever is greater, on \u001b[0m\n", - "\u001b[32mtransfers made within 60 days of account opening. after that: either $5 or 5% of the amount of each transfer, \u001b[0m\n", - "\u001b[32mwhichever is greater.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m5.0\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'cash advances'\u001b[0m,\n", - " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Either $10 or 5% of the amount of each transaction, whichever is greater.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m5.0\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'foreign transactions'\u001b[0m,\n", - " \u001b[32m'explanation'\u001b[0m: \u001b[32m'3% of the amount of each transaction in U.S. dollars.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m3.0\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'Late Payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'over-the-credit-limit penalty'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'Return Payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'Return Check'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m\n", - " \u001b[1m]\u001b[0m,\n", - " \u001b[32m'interest_rates'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'purchase annual percentage rate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mapr\u001b[0m\u001b[32m)\u001b[0m\u001b[32m'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'introductory rate'\u001b[0m: \u001b[1;36m0.0\u001b[0m,\n", - " \u001b[32m'introductory period'\u001b[0m: \u001b[32m'first 18 months'\u001b[0m,\n", - " \u001b[32m'standard rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The interest rate charged on purchases made with the credit card.'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[32m'my chase loan sm apr'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The interest rate charged on My Chase Loan balances.'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[32m'balance transfer apr'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'introductory rate'\u001b[0m: \u001b[1;36m0.0\u001b[0m,\n", - " \u001b[32m'introductory period'\u001b[0m: \u001b[32m'first 18 months'\u001b[0m,\n", - " \u001b[32m'standard rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The interest rate charged on balances transferred to the credit card.'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[32m'cash advance apr'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.49\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The interest rate charged on cash advances taken with the credit card.'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[32m'penalty apr and when it applies'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'rate'\u001b[0m: \u001b[32m'Up to 29.99%'\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The interest rate charged on the credit card when a penalty is applied.'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[32m'maximum apr'\u001b[0m: \u001b[1m{\u001b[0m\n", - " \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.99\u001b[0m,\n", - " \u001b[32m'variable'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", - " \u001b[32m'description'\u001b[0m: \u001b[32m'The highest interest rate that can be charged on the credit card.'\u001b[0m\n", - " \u001b[1m}\u001b[0m\n", - " \u001b[1m}\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" + " \u001b[32m'value'\u001b[0m: \u001b[1;36m1.72\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'balance transfers intro fee'\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Either $5 or 3% of the amount of each transfer, whichever is greater, on transfers \u001b[0m\n", + "\u001b[32mmade within 60 days of account opening. After that: Either $5 or 5% of the amount of each transfer, whichever is \u001b[0m\n", + "\u001b[32mgreater.'\u001b[0m,\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m5.0\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'cash advances'\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Either $10 or 5% of the amount of each transaction, whichever is greater.'\u001b[0m,\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m5.0\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'foreign transactions'\u001b[0m,\n", + " \u001b[32m'explanation'\u001b[0m: \u001b[32m'3% of the amount of each transaction in U.S. dollars.'\u001b[0m,\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m3.0\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'late payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m40.0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'over-the-credit-limit'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'return payment'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'Up to $40.'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m40.0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'return check'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[3;35mNone\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[32m'interest_rates'\u001b[0m: \u001b[3;35mNone\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", + " \u001b[33mfail_results\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mFailResult\u001b[0m\u001b[1m(\u001b[0m\u001b[33moutcome\u001b[0m=\u001b[32m'fail'\u001b[0m, \u001b[33mmetadata\u001b[0m=\u001b[3;35mNone\u001b[0m, \u001b[33merror_message\u001b[0m=\u001b[32m'JSON does not match schema'\u001b[0m, \u001b[33mfix_value\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, "metadata": {}, @@ -620,7 +639,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -629,7 +648,6 @@ "
Logs\n",
        "├── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "│   │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
-       "│   │ │                                                                                                         │ │\n",
        "│   │ │ Given the following document, answer the following questions. If the answer doesn't exist in the        │ │\n",
        "│   │ │ document, enter                                                                                         │ │\n",
        "│   │ │ `null`.                                                                                                 │ │\n",
@@ -777,10 +795,8 @@
        "│   │ │ savings and checking accounts, loans, and credit products?\"/>                                           │ │\n",
        "│   │ │ </output>                                                                                               │ │\n",
        "│   │ │                                                                                                         │ │\n",
-       "│   │ │                                                                                                         │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n",
-       "│   │ │                                                                                                         │ │\n",
        "│   │ │ You are a helpful assistant only capable of communicating with valid JSON, and no other text.           │ │\n",
        "│   │ │                                                                                                         │ │\n",
        "│   │ │                                                                                                         │ │\n",
@@ -797,256 +813,157 @@
        "│   │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "│   │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "│   │ │                                                                                                         │ │\n",
-       "│   │ │                                                                                                         │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "│   │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "│   │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "│   │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "│   │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "│   │ │ {                                                                                                       │ │\n",
-       "│   │ │     \"fees\": [                                                                                           │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 1,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"annual membership fee\",                                                            │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 2,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"my chase plan sm fee\",                                                             │ │\n",
-       "│   │ │             \"explanation\": \"monthly fee of 0% of the amount of each eligible purchase transaction or    │ │\n",
-       "│   │ │ amount selected to create a my chase plan while in the 0% intro purchase apr period. after that,        │ │\n",
-       "│   │ │ monthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a │ │\n",
-       "│   │ │ my chase plan. the my chase plan fee will be determined at the time each my chase plan is created and   │ │\n",
-       "│   │ │ will remain the same until the my chase plan is paid in full.\",                                         │ │\n",
-       "│   │ │             \"value\": 1.72                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 3,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"balance transfers transaction fee\",                                                │ │\n",
-       "│   │ │             \"explanation\": \"intro fee of either $5 or 3% of the amount of each transfer, whichever is   │ │\n",
-       "│   │ │ greater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount │ │\n",
-       "│   │ │ of each transfer, whichever is greater.\",                                                               │ │\n",
-       "│   │ │             \"value\": 5.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 4,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"cash advances transaction fee\",                                                    │ │\n",
-       "│   │ │             \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\", │ │\n",
-       "│   │ │             \"value\": 5.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 5,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"foreign transactions transaction fee\",                                             │ │\n",
-       "│   │ │             \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                     │ │\n",
-       "│   │ │             \"value\": 3.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 6,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"late payment penalty fee\",                                                         │ │\n",
-       "│   │ │             \"explanation\": \"Up to $40.\",                                                                │ │\n",
-       "│   │ │             \"value\": null                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 7,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"over-the-credit-limit penalty fee\",                                                │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": null                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 8,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"return payment penalty fee\",                                                       │ │\n",
-       "│   │ │             \"explanation\": \"Up to $40.\",                                                                │ │\n",
-       "│   │ │             \"value\": null                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"index\": 9,                                                                                 │ │\n",
-       "│   │ │             \"name\": \"return check penalty fee\",                                                         │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": null                                                                               │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
-       "│   │ │     ],                                                                                                  │ │\n",
-       "│   │ │     \"interest_rates\": {                                                                                 │ │\n",
-       "│   │ │         \"purchase annual percentage rate (apr)\": {                                                      │ │\n",
-       "│   │ │             \"introductory rate\": 0.0,                                                                   │ │\n",
-       "│   │ │             \"introductory period\": \"first 18 months\",                                                   │ │\n",
-       "│   │ │             \"standard rate\": 19.49,                                                                     │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The interest rate charged on purchases made with the credit card.\"          │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         \"my chase loan sm apr\": {                                                                       │ │\n",
-       "│   │ │             \"rate\": 19.49,                                                                              │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The interest rate charged on My Chase Loan balances.\"                       │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         \"balance transfer apr\": {                                                                       │ │\n",
-       "│   │ │             \"introductory rate\": 0.0,                                                                   │ │\n",
-       "│   │ │             \"introductory period\": \"first 18 months\",                                                   │ │\n",
-       "│   │ │             \"standard rate\": 19.49,                                                                     │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The interest rate charged on balances transferred to the credit card.\"      │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         \"cash advance apr\": {                                                                           │ │\n",
-       "│   │ │             \"rate\": 29.49,                                                                              │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The interest rate charged on cash advances taken with the credit card.\"     │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         \"penalty apr and when it applies\": {                                                            │ │\n",
-       "│   │ │             \"rate\": \"Up to 29.99%\",                                                                     │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The interest rate charged on the credit card when a penalty is applied.\"    │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         \"maximum apr\": {                                                                                │ │\n",
-       "│   │ │             \"rate\": 29.99,                                                                              │ │\n",
-       "│   │ │             \"variable\": true,                                                                           │ │\n",
-       "│   │ │             \"description\": \"The highest interest rate that can be charged on the credit card.\"          │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
+       "│   │ │   \"fees\": [                                                                                             │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 1,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"annual membership fee\",                                                                  │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 2,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"my chase plan fee\",                                                                      │ │\n",
+       "│   │ │       \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount   │ │\n",
+       "│   │ │ selected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that, monthly fee  │ │\n",
+       "│   │ │ of 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase    │ │\n",
+       "│   │ │ Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will       │ │\n",
+       "│   │ │ remain the same until the My Chase Plan is paid in full.\",                                              │ │\n",
+       "│   │ │       \"value\": 1.72                                                                                     │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 3,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"balance transfers intro fee\",                                                            │ │\n",
+       "│   │ │       \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on          │ │\n",
+       "│   │ │ transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each     │ │\n",
+       "│   │ │ transfer, whichever is greater.\",                                                                       │ │\n",
+       "│   │ │       \"value\": 5                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 4,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"cash advances\",                                                                          │ │\n",
+       "│   │ │       \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",       │ │\n",
+       "│   │ │       \"value\": 5                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 5,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"foreign transactions\",                                                                   │ │\n",
+       "│   │ │       \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                           │ │\n",
+       "│   │ │       \"value\": 3                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 6,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"late payment\",                                                                           │ │\n",
+       "│   │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "│   │ │       \"value\": 40                                                                                       │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 7,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"over-the-credit-limit\",                                                                  │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": null                                                                                     │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 8,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"return payment\",                                                                         │ │\n",
+       "│   │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "│   │ │       \"value\": 40                                                                                       │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"index\": 9,                                                                                       │ │\n",
+       "│   │ │       \"name\": \"return check\",                                                                           │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": null                                                                                     │ │\n",
        "│   │ │     }                                                                                                   │ │\n",
+       "│   │ │   ],                                                                                                    │ │\n",
+       "│   │ │   \"interest_rates\": null                                                                                │ │\n",
        "│   │ │ }                                                                                                       │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
-       "│   │ │ {                                                                                                       │ │\n",
-       "│   │ │     'fees': [                                                                                           │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 1,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='annual membership fee',                                                │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='annual membership',                                                          │ │\n",
-       "│   │ │                 path=['fees', 0, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'None',                                                                      │ │\n",
-       "│   │ │             'value': 0.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 2,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='my chase plan sm fee',                                                 │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='my chase',                                                                   │ │\n",
-       "│   │ │                 path=['fees', 1, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'monthly fee of 0% of the amount of each eligible purchase transaction or    │ │\n",
-       "│   │ │ amount selected to create a my chase plan while in the 0% intro purchase apr period. after that,        │ │\n",
+       "│   │ │ SkeletonReAsk(                                                                                          │ │\n",
+       "│   │ │     incorrect_value={                                                                                   │ │\n",
+       "│   │ │         'fees': [                                                                                       │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 1,                                                                             │ │\n",
+       "│   │ │                 'name': 'annual membership fee',                                                        │ │\n",
+       "│   │ │                 'explanation': 'None',                                                                  │ │\n",
+       "│   │ │                 'value': 0.0                                                                            │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 2,                                                                             │ │\n",
+       "│   │ │                 'name': 'my chase plan fee',                                                            │ │\n",
+       "│   │ │                 'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction   │ │\n",
+       "│   │ │ or amount selected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that,    │ │\n",
        "│   │ │ monthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a │ │\n",
-       "│   │ │ my chase plan. the my chase plan fee will be determined at the time each my chase plan is created and   │ │\n",
-       "│   │ │ will remain the same until the my chase plan is paid in full.',                                         │ │\n",
-       "│   │ │             'value': 1.72                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 3,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='balance transfers transaction fee',                                    │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='balance transfers',                                                          │ │\n",
-       "│   │ │                 path=['fees', 2, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'intro fee of either $5 or 3% of the amount of each transfer, whichever is   │ │\n",
-       "│   │ │ greater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount │ │\n",
-       "│   │ │ of each transfer, whichever is greater.',                                                               │ │\n",
-       "│   │ │             'value': 5.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 4,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='cash advances transaction fee',                                        │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='cash advances',                                                              │ │\n",
-       "│   │ │                 path=['fees', 3, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.', │ │\n",
-       "│   │ │             'value': 5.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 5,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='foreign transactions transaction fee',                                 │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='foreign transactions',                                                       │ │\n",
-       "│   │ │                 path=['fees', 4, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': '3% of the amount of each transaction in U.S. dollars.',                     │ │\n",
-       "│   │ │             'value': 3.0                                                                                │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 6,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='late payment penalty fee',                                             │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='late payment',                                                               │ │\n",
-       "│   │ │                 path=['fees', 5, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'Up to $40.',                                                                │ │\n",
-       "│   │ │             'value': None                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 7,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='over-the-credit-limit penalty fee',                                    │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='over-the-credit-limit penalty',                                              │ │\n",
-       "│   │ │                 path=['fees', 6, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'None',                                                                      │ │\n",
-       "│   │ │             'value': None                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 8,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='return payment penalty fee',                                           │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='return payment',                                                             │ │\n",
-       "│   │ │                 path=['fees', 7, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'Up to $40.',                                                                │ │\n",
-       "│   │ │             'value': None                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             'index': 9,                                                                                 │ │\n",
-       "│   │ │             'name': ReAsk(                                                                              │ │\n",
-       "│   │ │                 incorrect_value='return check penalty fee',                                             │ │\n",
-       "│   │ │                 error_message='must be exactly two words',                                              │ │\n",
-       "│   │ │                 fix_value='return check',                                                               │ │\n",
-       "│   │ │                 path=['fees', 8, 'name']                                                                │ │\n",
-       "│   │ │             ),                                                                                          │ │\n",
-       "│   │ │             'explanation': 'None',                                                                      │ │\n",
-       "│   │ │             'value': None                                                                               │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
-       "│   │ │     ],                                                                                                  │ │\n",
-       "│   │ │     'interest_rates': {                                                                                 │ │\n",
-       "│   │ │         'purchase annual percentage rate (apr)': {                                                      │ │\n",
-       "│   │ │             'introductory rate': 0.0,                                                                   │ │\n",
-       "│   │ │             'introductory period': 'first 18 months',                                                   │ │\n",
-       "│   │ │             'standard rate': 19.49,                                                                     │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The interest rate charged on purchases made with the credit card.'          │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         'my chase loan sm apr': {                                                                       │ │\n",
-       "│   │ │             'rate': 19.49,                                                                              │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The interest rate charged on My Chase Loan balances.'                       │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         'balance transfer apr': {                                                                       │ │\n",
-       "│   │ │             'introductory rate': 0.0,                                                                   │ │\n",
-       "│   │ │             'introductory period': 'first 18 months',                                                   │ │\n",
-       "│   │ │             'standard rate': 19.49,                                                                     │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The interest rate charged on balances transferred to the credit card.'      │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         'cash advance apr': {                                                                           │ │\n",
-       "│   │ │             'rate': 29.49,                                                                              │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The interest rate charged on cash advances taken with the credit card.'     │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         'penalty apr and when it applies': {                                                            │ │\n",
-       "│   │ │             'rate': 'Up to 29.99%',                                                                     │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The interest rate charged on the credit card when a penalty is applied.'    │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         'maximum apr': {                                                                                │ │\n",
-       "│   │ │             'rate': 29.99,                                                                              │ │\n",
-       "│   │ │             'variable': True,                                                                           │ │\n",
-       "│   │ │             'description': 'The highest interest rate that can be charged on the credit card.'          │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
-       "│   │ │     }                                                                                                   │ │\n",
-       "│   │ │ }                                                                                                       │ │\n",
+       "│   │ │ My Chase Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and   │ │\n",
+       "│   │ │ will remain the same until the My Chase Plan is paid in full.',                                         │ │\n",
+       "│   │ │                 'value': 1.72                                                                           │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 3,                                                                             │ │\n",
+       "│   │ │                 'name': 'balance transfers intro fee',                                                  │ │\n",
+       "│   │ │                 'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater,   │ │\n",
+       "│   │ │ on transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each  │ │\n",
+       "│   │ │ transfer, whichever is greater.',                                                                       │ │\n",
+       "│   │ │                 'value': 5.0                                                                            │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 4,                                                                             │ │\n",
+       "│   │ │                 'name': 'cash advances',                                                                │ │\n",
+       "│   │ │                 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is        │ │\n",
+       "│   │ │ greater.',                                                                                              │ │\n",
+       "│   │ │                 'value': 5.0                                                                            │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 5,                                                                             │ │\n",
+       "│   │ │                 'name': 'foreign transactions',                                                         │ │\n",
+       "│   │ │                 'explanation': '3% of the amount of each transaction in U.S. dollars.',                 │ │\n",
+       "│   │ │                 'value': 3.0                                                                            │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 6,                                                                             │ │\n",
+       "│   │ │                 'name': 'late payment',                                                                 │ │\n",
+       "│   │ │                 'explanation': 'Up to $40.',                                                            │ │\n",
+       "│   │ │                 'value': 40.0                                                                           │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 7,                                                                             │ │\n",
+       "│   │ │                 'name': 'over-the-credit-limit',                                                        │ │\n",
+       "│   │ │                 'explanation': 'None',                                                                  │ │\n",
+       "│   │ │                 'value': None                                                                           │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 8,                                                                             │ │\n",
+       "│   │ │                 'name': 'return payment',                                                               │ │\n",
+       "│   │ │                 'explanation': 'Up to $40.',                                                            │ │\n",
+       "│   │ │                 'value': 40                                                                             │ │\n",
+       "│   │ │             },                                                                                          │ │\n",
+       "│   │ │             {                                                                                           │ │\n",
+       "│   │ │                 'index': 9,                                                                             │ │\n",
+       "│   │ │                 'name': 'return check',                                                                 │ │\n",
+       "│   │ │                 'explanation': 'None',                                                                  │ │\n",
+       "│   │ │                 'value': None                                                                           │ │\n",
+       "│   │ │             }                                                                                           │ │\n",
+       "│   │ │         ],                                                                                              │ │\n",
+       "│   │ │         'interest_rates': None                                                                          │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     fail_results=[                                                                                      │ │\n",
+       "│   │ │         FailResult(                                                                                     │ │\n",
+       "│   │ │             outcome='fail',                                                                             │ │\n",
+       "│   │ │             metadata=None,                                                                              │ │\n",
+       "│   │ │             error_message='JSON does not match schema',                                                 │ │\n",
+       "│   │ │             fix_value=None                                                                              │ │\n",
+       "│   │ │         )                                                                                               │ │\n",
+       "│   │ │     ]                                                                                                   │ │\n",
+       "│   │ │ )                                                                                                       │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
        "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
@@ -1055,61 +972,73 @@
        "    │ │ I was given the following JSON response, which had problems due to incorrect values.                    │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ {                                                                                                       │ │\n",
-       "    │ │   \"fees\": [                                                                                             │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"annual membership fee\",                                                     │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
+       "    │ │   \"incorrect_value\": {                                                                                  │ │\n",
+       "    │ │     \"fees\": [                                                                                           │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 1,                                                                                     │ │\n",
+       "    │ │         \"name\": \"annual membership fee\",                                                                │ │\n",
+       "    │ │         \"explanation\": \"None\",                                                                          │ │\n",
+       "    │ │         \"value\": 0.0                                                                                    │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 2,                                                                                     │ │\n",
+       "    │ │         \"name\": \"my chase plan fee\",                                                                    │ │\n",
+       "    │ │         \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount │ │\n",
+       "    │ │ selected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that, monthly fee  │ │\n",
+       "    │ │ of 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase    │ │\n",
+       "    │ │ Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will       │ │\n",
+       "    │ │ remain the same until the My Chase Plan is paid in full.\",                                              │ │\n",
+       "    │ │         \"value\": 1.72                                                                                   │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 3,                                                                                     │ │\n",
+       "    │ │         \"name\": \"balance transfers intro fee\",                                                          │ │\n",
+       "    │ │         \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on        │ │\n",
+       "    │ │ transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each     │ │\n",
+       "    │ │ transfer, whichever is greater.\",                                                                       │ │\n",
+       "    │ │         \"value\": 5.0                                                                                    │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 4,                                                                                     │ │\n",
+       "    │ │         \"name\": \"cash advances\",                                                                        │ │\n",
+       "    │ │         \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",     │ │\n",
+       "    │ │         \"value\": 5.0                                                                                    │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 5,                                                                                     │ │\n",
+       "    │ │         \"name\": \"foreign transactions\",                                                                 │ │\n",
+       "    │ │         \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                         │ │\n",
+       "    │ │         \"value\": 3.0                                                                                    │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 6,                                                                                     │ │\n",
+       "    │ │         \"name\": \"late payment\",                                                                         │ │\n",
+       "    │ │         \"explanation\": \"Up to $40.\",                                                                    │ │\n",
+       "    │ │         \"value\": 40.0                                                                                   │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 7,                                                                                     │ │\n",
+       "    │ │         \"name\": \"over-the-credit-limit\",                                                                │ │\n",
+       "    │ │         \"explanation\": \"None\",                                                                          │ │\n",
+       "    │ │         \"value\": null                                                                                   │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 8,                                                                                     │ │\n",
+       "    │ │         \"name\": \"return payment\",                                                                       │ │\n",
+       "    │ │         \"explanation\": \"Up to $40.\",                                                                    │ │\n",
+       "    │ │         \"value\": 40                                                                                     │ │\n",
+       "    │ │       },                                                                                                │ │\n",
+       "    │ │       {                                                                                                 │ │\n",
+       "    │ │         \"index\": 9,                                                                                     │ │\n",
+       "    │ │         \"name\": \"return check\",                                                                         │ │\n",
+       "    │ │         \"explanation\": \"None\",                                                                          │ │\n",
+       "    │ │         \"value\": null                                                                                   │ │\n",
        "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"my chase plan sm fee\",                                                      │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"balance transfers transaction fee\",                                         │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"cash advances transaction fee\",                                             │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"foreign transactions transaction fee\",                                      │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"late payment penalty fee\",                                                  │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"over-the-credit-limit penalty fee\",                                         │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"return payment penalty fee\",                                                │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     },                                                                                                  │ │\n",
-       "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"return check penalty fee\",                                                  │ │\n",
-       "    │ │         \"error_message\": \"must be exactly two words\"                                                    │ │\n",
-       "    │ │       }                                                                                                 │ │\n",
-       "    │ │     }                                                                                                   │ │\n",
+       "    │ │     ],                                                                                                  │ │\n",
+       "    │ │     \"interest_rates\": null                                                                              │ │\n",
+       "    │ │   },                                                                                                    │ │\n",
+       "    │ │   \"error_messages\": [                                                                                   │ │\n",
+       "    │ │     \"JSON does not match schema\"                                                                        │ │\n",
        "    │ │   ]                                                                                                     │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -1121,9 +1050,14 @@
        "    │ │ <output>                                                                                                │ │\n",
        "    │ │     <list name=\"fees\" description=\"What fees and charges are associated with my account?\">              │ │\n",
        "    │ │         <object>                                                                                        │ │\n",
+       "    │ │             <integer name=\"index\" format=\"1-indexed\"/>                                                  │ │\n",
        "    │ │             <string name=\"name\" format=\"lower-case; two-words\"/>                                        │ │\n",
+       "    │ │             <string name=\"explanation\" format=\"one-line\"/>                                              │ │\n",
+       "    │ │             <float name=\"value\" format=\"percentage\"/>                                                   │ │\n",
        "    │ │         </object>                                                                                       │ │\n",
        "    │ │     </list>                                                                                             │ │\n",
+       "    │ │     <object name=\"interest_rates\" description=\"What are the interest rates offered by the bank on       │ │\n",
+       "    │ │ savings and checking accounts, loans, and credit products?\"/>                                           │ │\n",
        "    │ │ </output>                                                                                               │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -1133,19 +1067,11 @@
        "    │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere,     │ │\n",
        "    │ │ enter `null`.                                                                                           │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior:                          │ │\n",
-       "    │ │ - `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`                   │ │\n",
-       "    │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO',    │ │\n",
-       "    │ │ etc.]}}`                                                                                                │ │\n",
-       "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
-       "    │ │ format=\"1-indexed\" /></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`                    │ │\n",
-       "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ You are a helpful assistant only capable of communicating with valid JSON, and no other text.           │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │                                                                                                         │ │\n",
        "    │ │ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the │ │\n",
        "    │ │ `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding  │ │\n",
        "    │ │ XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g.        │ │\n",
@@ -1159,142 +1085,154 @@
        "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ No message history.                                                                                     │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "    │ │ {                                                                                                       │ │\n",
        "    │ │   \"fees\": [                                                                                             │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Annual Membership\"                                                                       │ │\n",
+       "    │ │       \"index\": 1,                                                                                       │ │\n",
+       "    │ │       \"name\": \"annual membership fee\",                                                                  │ │\n",
+       "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "    │ │       \"value\": 0.0                                                                                      │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"My Chase Plan Sm\"                                                                        │ │\n",
+       "    │ │       \"index\": 2,                                                                                       │ │\n",
+       "    │ │       \"name\": \"my chase plan fee\",                                                                      │ │\n",
+       "    │ │       \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount   │ │\n",
+       "    │ │ selected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that, monthly fee   │ │\n",
+       "    │ │ of 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase    │ │\n",
+       "    │ │ Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will       │ │\n",
+       "    │ │ remain the same until the My Chase Plan is paid in full.\",                                              │ │\n",
+       "    │ │       \"value\": 1.72                                                                                     │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Balance Transfers\"                                                                       │ │\n",
+       "    │ │       \"index\": 3,                                                                                       │ │\n",
+       "    │ │       \"name\": \"balance transfers intro fee\",                                                            │ │\n",
+       "    │ │       \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on          │ │\n",
+       "    │ │ transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each     │ │\n",
+       "    │ │ transfer, whichever is greater.\",                                                                       │ │\n",
+       "    │ │       \"value\": 5.0                                                                                      │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Cash Advances\"                                                                           │ │\n",
+       "    │ │       \"index\": 4,                                                                                       │ │\n",
+       "    │ │       \"name\": \"cash advances\",                                                                          │ │\n",
+       "    │ │       \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",       │ │\n",
+       "    │ │       \"value\": 5.0                                                                                      │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Foreign Transactions\"                                                                    │ │\n",
+       "    │ │       \"index\": 5,                                                                                       │ │\n",
+       "    │ │       \"name\": \"foreign transactions\",                                                                   │ │\n",
+       "    │ │       \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                           │ │\n",
+       "    │ │       \"value\": 3.0                                                                                      │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Late Payment Penalty\"                                                                    │ │\n",
+       "    │ │       \"index\": 6,                                                                                       │ │\n",
+       "    │ │       \"name\": \"late payment\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "    │ │       \"value\": 40.0                                                                                     │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Over-The-Credit-Limit Penalty\"                                                           │ │\n",
+       "    │ │       \"index\": 7,                                                                                       │ │\n",
+       "    │ │       \"name\": \"over-the-credit-limit\",                                                                  │ │\n",
+       "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "    │ │       \"value\": null                                                                                     │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Return Payment Penalty\"                                                                  │ │\n",
+       "    │ │       \"index\": 8,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return payment\",                                                                         │ │\n",
+       "    │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "    │ │       \"value\": 40.0                                                                                     │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"Return Check Penalty\"                                                                    │ │\n",
+       "    │ │       \"index\": 9,                                                                                       │ │\n",
+       "    │ │       \"name\": \"return check\",                                                                           │ │\n",
+       "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "    │ │       \"value\": null                                                                                     │ │\n",
        "    │ │     }                                                                                                   │ │\n",
-       "    │ │   ]                                                                                                     │ │\n",
+       "    │ │   ],                                                                                                    │ │\n",
+       "    │ │   \"interest_rates\": null                                                                                │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
-       "    │ │ {                                                                                                       │ │\n",
-       "    │ │     'fees': [                                                                                           │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 1,                                                                                 │ │\n",
-       "    │ │             'name': 'annual membership',                                                                │ │\n",
-       "    │ │             'explanation': 'None',                                                                      │ │\n",
-       "    │ │             'value': 0.0                                                                                │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 2,                                                                                 │ │\n",
-       "    │ │             'name': 'My Chase',                                                                         │ │\n",
-       "    │ │             'explanation': 'monthly fee of 0% of the amount of each eligible purchase transaction or    │ │\n",
-       "    │ │ amount selected to create a my chase plan while in the 0% intro purchase apr period. after that,        │ │\n",
+       "    │ │ SkeletonReAsk(                                                                                          │ │\n",
+       "    │ │     incorrect_value={                                                                                   │ │\n",
+       "    │ │         'fees': [                                                                                       │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 1,                                                                             │ │\n",
+       "    │ │                 'name': 'annual membership fee',                                                        │ │\n",
+       "    │ │                 'explanation': 'None',                                                                  │ │\n",
+       "    │ │                 'value': 0.0                                                                            │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 2,                                                                             │ │\n",
+       "    │ │                 'name': 'my chase plan fee',                                                            │ │\n",
+       "    │ │                 'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction   │ │\n",
+       "    │ │ or amount selected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that,     │ │\n",
        "    │ │ monthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a │ │\n",
-       "    │ │ my chase plan. the my chase plan fee will be determined at the time each my chase plan is created and   │ │\n",
-       "    │ │ will remain the same until the my chase plan is paid in full.',                                         │ │\n",
-       "    │ │             'value': 1.72                                                                               │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 3,                                                                                 │ │\n",
-       "    │ │             'name': 'balance transfers',                                                                │ │\n",
-       "    │ │             'explanation': 'intro fee of either $5 or 3% of the amount of each transfer, whichever is   │ │\n",
-       "    │ │ greater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount │ │\n",
-       "    │ │ of each transfer, whichever is greater.',                                                               │ │\n",
-       "    │ │             'value': 5.0                                                                                │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 4,                                                                                 │ │\n",
-       "    │ │             'name': 'cash advances',                                                                    │ │\n",
-       "    │ │             'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.', │ │\n",
-       "    │ │             'value': 5.0                                                                                │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 5,                                                                                 │ │\n",
-       "    │ │             'name': 'foreign transactions',                                                             │ │\n",
-       "    │ │             'explanation': '3% of the amount of each transaction in U.S. dollars.',                     │ │\n",
-       "    │ │             'value': 3.0                                                                                │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 6,                                                                                 │ │\n",
-       "    │ │             'name': 'Late Payment',                                                                     │ │\n",
-       "    │ │             'explanation': 'Up to $40.',                                                                │ │\n",
-       "    │ │             'value': None                                                                               │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 7,                                                                                 │ │\n",
-       "    │ │             'name': 'over-the-credit-limit penalty',                                                    │ │\n",
-       "    │ │             'explanation': 'None',                                                                      │ │\n",
-       "    │ │             'value': None                                                                               │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 8,                                                                                 │ │\n",
-       "    │ │             'name': 'Return Payment',                                                                   │ │\n",
-       "    │ │             'explanation': 'Up to $40.',                                                                │ │\n",
-       "    │ │             'value': None                                                                               │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 9,                                                                                 │ │\n",
-       "    │ │             'name': 'Return Check',                                                                     │ │\n",
-       "    │ │             'explanation': 'None',                                                                      │ │\n",
-       "    │ │             'value': None                                                                               │ │\n",
-       "    │ │         }                                                                                               │ │\n",
-       "    │ │     ],                                                                                                  │ │\n",
-       "    │ │     'interest_rates': {                                                                                 │ │\n",
-       "    │ │         'purchase annual percentage rate (apr)': {                                                      │ │\n",
-       "    │ │             'introductory rate': 0.0,                                                                   │ │\n",
-       "    │ │             'introductory period': 'first 18 months',                                                   │ │\n",
-       "    │ │             'standard rate': 19.49,                                                                     │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The interest rate charged on purchases made with the credit card.'          │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         'my chase loan sm apr': {                                                                       │ │\n",
-       "    │ │             'rate': 19.49,                                                                              │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The interest rate charged on My Chase Loan balances.'                       │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         'balance transfer apr': {                                                                       │ │\n",
-       "    │ │             'introductory rate': 0.0,                                                                   │ │\n",
-       "    │ │             'introductory period': 'first 18 months',                                                   │ │\n",
-       "    │ │             'standard rate': 19.49,                                                                     │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The interest rate charged on balances transferred to the credit card.'      │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         'cash advance apr': {                                                                           │ │\n",
-       "    │ │             'rate': 29.49,                                                                              │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The interest rate charged on cash advances taken with the credit card.'     │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         'penalty apr and when it applies': {                                                            │ │\n",
-       "    │ │             'rate': 'Up to 29.99%',                                                                     │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The interest rate charged on the credit card when a penalty is applied.'    │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         'maximum apr': {                                                                                │ │\n",
-       "    │ │             'rate': 29.99,                                                                              │ │\n",
-       "    │ │             'variable': True,                                                                           │ │\n",
-       "    │ │             'description': 'The highest interest rate that can be charged on the credit card.'          │ │\n",
-       "    │ │         }                                                                                               │ │\n",
-       "    │ │     }                                                                                                   │ │\n",
-       "    │ │ }                                                                                                       │ │\n",
+       "    │ │ My Chase Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and   │ │\n",
+       "    │ │ will remain the same until the My Chase Plan is paid in full.',                                         │ │\n",
+       "    │ │                 'value': 1.72                                                                           │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 3,                                                                             │ │\n",
+       "    │ │                 'name': 'balance transfers intro fee',                                                  │ │\n",
+       "    │ │                 'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater,   │ │\n",
+       "    │ │ on transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each  │ │\n",
+       "    │ │ transfer, whichever is greater.',                                                                       │ │\n",
+       "    │ │                 'value': 5.0                                                                            │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 4,                                                                             │ │\n",
+       "    │ │                 'name': 'cash advances',                                                                │ │\n",
+       "    │ │                 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is        │ │\n",
+       "    │ │ greater.',                                                                                              │ │\n",
+       "    │ │                 'value': 5.0                                                                            │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 5,                                                                             │ │\n",
+       "    │ │                 'name': 'foreign transactions',                                                         │ │\n",
+       "    │ │                 'explanation': '3% of the amount of each transaction in U.S. dollars.',                 │ │\n",
+       "    │ │                 'value': 3.0                                                                            │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 6,                                                                             │ │\n",
+       "    │ │                 'name': 'late payment',                                                                 │ │\n",
+       "    │ │                 'explanation': 'Up to $40.',                                                            │ │\n",
+       "    │ │                 'value': 40.0                                                                           │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 7,                                                                             │ │\n",
+       "    │ │                 'name': 'over-the-credit-limit',                                                        │ │\n",
+       "    │ │                 'explanation': 'None',                                                                  │ │\n",
+       "    │ │                 'value': None                                                                           │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 8,                                                                             │ │\n",
+       "    │ │                 'name': 'return payment',                                                               │ │\n",
+       "    │ │                 'explanation': 'Up to $40.',                                                            │ │\n",
+       "    │ │                 'value': 40.0                                                                           │ │\n",
+       "    │ │             },                                                                                          │ │\n",
+       "    │ │             {                                                                                           │ │\n",
+       "    │ │                 'index': 9,                                                                             │ │\n",
+       "    │ │                 'name': 'return check',                                                                 │ │\n",
+       "    │ │                 'explanation': 'None',                                                                  │ │\n",
+       "    │ │                 'value': None                                                                           │ │\n",
+       "    │ │             }                                                                                           │ │\n",
+       "    │ │         ],                                                                                              │ │\n",
+       "    │ │         'interest_rates': None                                                                          │ │\n",
+       "    │ │     },                                                                                                  │ │\n",
+       "    │ │     fail_results=[                                                                                      │ │\n",
+       "    │ │         FailResult(                                                                                     │ │\n",
+       "    │ │             outcome='fail',                                                                             │ │\n",
+       "    │ │             metadata=None,                                                                              │ │\n",
+       "    │ │             error_message='JSON does not match schema',                                                 │ │\n",
+       "    │ │             fix_value=None                                                                              │ │\n",
+       "    │ │         )                                                                                               │ │\n",
+       "    │ │     ]                                                                                                   │ │\n",
+       "    │ │ )                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
        "
\n" @@ -1303,7 +1241,6 @@ "Logs\n", "├── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", "│ │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n", - "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mGiven the following document, answer the following questions. If the answer doesn't exist in the \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mdocument, enter \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m`null`.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1451,10 +1388,8 @@ "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255msavings and checking accounts, loans, and credit products?\"/>\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n", - "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant only capable of communicating with valid JSON, and no other text.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", @@ -1471,256 +1406,157 @@ "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", - "│ │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", "│ │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"fees\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 1,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"annual membership fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 0.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 2,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"my chase plan sm fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"monthly fee of 0% of the amount of each eligible purchase transaction or \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mamount selected to create a my chase plan while in the 0% intro purchase apr period. after that, \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mmonthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mmy chase plan. the my chase plan fee will be determined at the time each my chase plan is created and \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mwill remain the same until the my chase plan is paid in full.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 1.72\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"balance transfers transaction fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"intro fee of either $5 or 3% of the amount of each transfer, whichever is \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgreater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mof each transfer, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"cash advances transaction fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"foreign transactions transaction fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 3.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"late payment penalty fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"over-the-credit-limit penalty fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 8,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return payment penalty fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 9,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return check penalty fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ],\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"interest_rates\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"purchase annual percentage rate (apr)\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"introductory rate\": 0.0,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"introductory period\": \"first 18 months\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"standard rate\": 19.49,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The interest rate charged on purchases made with the credit card.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"my chase loan sm apr\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"rate\": 19.49,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The interest rate charged on My Chase Loan balances.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"balance transfer apr\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"introductory rate\": 0.0,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"introductory period\": \"first 18 months\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"standard rate\": 19.49,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The interest rate charged on balances transferred to the credit card.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"cash advance apr\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"rate\": 29.49,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The interest rate charged on cash advances taken with the credit card.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"penalty apr and when it applies\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"rate\": \"Up to 29.99%\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The interest rate charged on the credit card when a penalty is applied.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"maximum apr\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"rate\": 29.99,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"variable\": true,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"description\": \"The highest interest rate that can be charged on the credit card.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"fees\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 1,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"annual membership fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 2,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"my chase plan fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mselected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that, monthly fee \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mof 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPlan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mremain the same until the My Chase Plan is paid in full.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 1.72\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"balance transfers intro fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtransfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtransfer, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"cash advances\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 3\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"late payment\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 40\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"over-the-credit-limit\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 8,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return payment\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 40\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 9,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return check\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ],\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"interest_rates\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'fees': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 1,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='annual membership fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='annual membership',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 0, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 0.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 2,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='my chase plan sm fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='my chase',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 1, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'monthly fee of 0% of the amount of each eligible purchase transaction or \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mamount selected to create a my chase plan while in the 0% intro purchase apr period. after that, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mSkeletonReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value={\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'fees': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 1,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'annual membership fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 0.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 2,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'my chase plan fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mor amount selected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mmonthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mmy chase plan. the my chase plan fee will be determined at the time each my chase plan is created and \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwill remain the same until the my chase plan is paid in full.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 1.72\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 3,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='balance transfers transaction fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='balance transfers',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 2, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'intro fee of either $5 or 3% of the amount of each transfer, whichever is \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mgreater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mof each transfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 4,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='cash advances transaction fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='cash advances',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 3, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 5,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='foreign transactions transaction fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='foreign transactions',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 4, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': '3% of the amount of each transaction in U.S. dollars.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 3.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 6,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='late payment penalty fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='late payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 5, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 7,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='over-the-credit-limit penalty fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='over-the-credit-limit penalty',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 6, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 8,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='return payment penalty fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='return payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 7, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 9,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='return check penalty fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='must be exactly two words',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value='return check',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['fees', 8, 'name']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ),\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'interest_rates': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'purchase annual percentage rate (apr)': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory rate': 0.0,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory period': 'first 18 months',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'standard rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on purchases made with the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'my chase loan sm apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on My Chase Loan balances.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'balance transfer apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory rate': 0.0,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory period': 'first 18 months',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'standard rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on balances transferred to the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'cash advance apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 29.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on cash advances taken with the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'penalty apr and when it applies': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 'Up to 29.99%',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on the credit card when a penalty is applied.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'maximum apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 29.99,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The highest interest rate that can be charged on the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mMy Chase Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwill remain the same until the My Chase Plan is paid in full.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 1.72\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 3,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'balance transfers intro fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mon transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtransfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 4,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'cash advances',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mgreater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 5,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'foreign transactions',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': '3% of the amount of each transaction in U.S. dollars.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 3.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 6,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'late payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 40.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 7,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'over-the-credit-limit',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 8,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'return payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 40\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 9,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'return check',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'interest_rates': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='JSON does not match schema',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m)\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", "│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n", @@ -1729,61 +1565,73 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mI was given the following JSON response, which had problems due to incorrect values.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m{\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"fees\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"annual membership fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"my chase plan sm fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"balance transfers transaction fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"cash advances transaction fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"foreign transactions transaction fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"late payment penalty fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"fees\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 1,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"annual membership fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"None\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 0.0\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 2,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"my chase plan fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mselected to create a My Chase Plan while in the 0% Intro Purchase APR period.\\nAfter that, monthly fee \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mof 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mPlan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mremain the same until the My Chase Plan is paid in full.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 1.72\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 3,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"balance transfers intro fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mtransfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mtransfer, whichever is greater.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 5.0\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 4,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"cash advances\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 5.0\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 5,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 3.0\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 6,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"late payment\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 40.0\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 7,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"over-the-credit-limit\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"None\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": null\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 8,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"return payment\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": 40\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"index\": 9,\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": \"return check\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"explanation\": \"None\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"value\": null\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"over-the-credit-limit penalty fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"return payment penalty fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"name\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"return check penalty fee\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"must be exactly two words\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ],\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"interest_rates\": null\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"JSON does not match schema\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m}\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1795,9 +1643,14 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1807,19 +1660,11 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mrequests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255menter `null`.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'foo': 'example one'}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255metc.]}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant only capable of communicating with valid JSON, and no other text.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", - " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m`name` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", @@ -1833,147 +1678,159 @@ " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", - " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", " │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235mNo message history.\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"fees\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Annual Membership\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 1,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"annual membership fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 0.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"My Chase Plan Sm\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 2,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"my chase plan fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Monthly fee of 0% of the amount of each eligible purchase transaction or amount \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mselected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that, monthly fee \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mof 1.72% of the amount of each eligible purchase transaction or amount selected to create a My Chase \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPlan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and will \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mremain the same until the My Chase Plan is paid in full.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 1.72\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Balance Transfers\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"balance transfers intro fee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtransfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtransfer, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Cash Advances\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"cash advances\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 5.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Foreign Transactions\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"foreign transactions\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 3.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Late Payment Penalty\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"late payment\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 40.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Over-The-Credit-Limit Penalty\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"over-the-credit-limit\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Return Payment Penalty\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 8,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return payment\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"Up to $40.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": 40.0\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"Return Check Penalty\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"index\": 9,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"name\": \"return check\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"explanation\": \"None\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"value\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ]\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ],\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"interest_rates\": null\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'fees': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 1,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'annual membership',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 0.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 2,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'My Chase',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'monthly fee of 0% of the amount of each eligible purchase transaction or \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mamount selected to create a my chase plan while in the 0% intro purchase apr period. after that, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mSkeletonReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value={\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'fees': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 1,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'annual membership fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 0.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 2,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'my chase plan fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mor amount selected to create a My Chase Plan while in the 0% Intro Purchase APR period. After that, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mmonthly fee of 1.72% of the amount of each eligible purchase transaction or amount selected to create a\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mmy chase plan. the my chase plan fee will be determined at the time each my chase plan is created and \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwill remain the same until the my chase plan is paid in full.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 1.72\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 3,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'balance transfers',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'intro fee of either $5 or 3% of the amount of each transfer, whichever is \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mgreater, on transfers made within 60 days of account opening. after that: either $5 or 5% of the amount\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mof each transfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 4,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'cash advances',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 5,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'foreign transactions',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': '3% of the amount of each transaction in U.S. dollars.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 3.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 6,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'Late Payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 7,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'over-the-credit-limit penalty',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 8,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'Return Payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 9,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'Return Check',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'interest_rates': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'purchase annual percentage rate (apr)': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory rate': 0.0,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory period': 'first 18 months',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'standard rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on purchases made with the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'my chase loan sm apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on My Chase Loan balances.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'balance transfer apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory rate': 0.0,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'introductory period': 'first 18 months',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'standard rate': 19.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on balances transferred to the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'cash advance apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 29.49,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on cash advances taken with the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'penalty apr and when it applies': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 'Up to 29.99%',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The interest rate charged on the credit card when a penalty is applied.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'maximum apr': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'rate': 29.99,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'variable': True,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'description': 'The highest interest rate that can be charged on the credit card.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mMy Chase Plan. The My Chase Plan Fee will be determined at the time each My Chase Plan is created and \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwill remain the same until the My Chase Plan is paid in full.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 1.72\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 3,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'balance transfers intro fee',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mon transfers made within 60 days of account opening. After that: Either $5 or 5% of the amount of each \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtransfer, whichever is greater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 4,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'cash advances',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Either $10 or 5% of the amount of each transaction, whichever is \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mgreater.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 5.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 5,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'foreign transactions',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': '3% of the amount of each transaction in U.S. dollars.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 3.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 6,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'late payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 40.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 7,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'over-the-credit-limit',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 8,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'return payment',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'Up to $40.',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': 40.0\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'index': 9,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'name': 'return check',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'explanation': 'None',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'value': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'interest_rates': None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message='JSON does not match schema',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m)\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] }, - "execution_count": 9, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1999,7 +1856,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" }, "orig_nbformat": 4 }, diff --git a/docs/examples/no_secrets_in_generated_text.ipynb b/docs/examples/no_secrets_in_generated_text.ipynb index 83ffe48de..a389769ef 100644 --- a/docs/examples/no_secrets_in_generated_text.ipynb +++ b/docs/examples/no_secrets_in_generated_text.ipynb @@ -12,7 +12,7 @@ "\n", "In this example, we will use Guardrails to generate strings that don't have any secrets.\n", "\n", - "This is also a good example to show how to use the `script` element of the `RAIL` specification. In this case, we will use the `script` element to create a custom Validator that checks if a string has any secrets.\n", + "This is also a good example to show how to use the custom Validators in the `RAIL` specification.\n", "\n", "## Objective\n", "\n", @@ -26,29 +26,31 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model.\n", "\n", "In this RAIL spec, we:\n", "\n", - "1. Create a `script` element that creates a custom Validator that checks if a string has any secrets. This is a simple example, but you can use this to create more complex Validators. For more information on creating custom Validators, see the [Validators documentation](../validation.md).\n", + "1. Create a custom Validator that checks if a string has any secrets. This is a simple example, but you can use this to create more complex Validators. For more information on creating custom Validators, see the [Validators documentation](../validation.md).\n", "2. Create a `output` schema that returns an object with a `api_help` key." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First the custom Validator:" + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ - "rail_str = \"\"\"\n", - "\n", - "\n", - "\n", + " return PassResult()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can use the validator in a Rail spec:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "rail_str = \"\"\"\n", + "\n", "\n", "\n", "\n", @@ -83,7 +98,7 @@ "\n", "How do I use OpenAI's Completion API?\n", "\n", - "@complete_json_suffix\n", + "${gr.complete_json_suffix}\n", "\n", "\n", "\n", @@ -91,6 +106,33 @@ "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or in a Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "\n", + "prompt = \"\"\"\n", + "\n", + "How do I use OpenAI's Completion API?\n", + "\n", + "${gr.complete_json_suffix}\n", + "\"\"\"\n", + "\n", + "class ScrubbedCode(BaseModel):\n", + " api_help: str = Field(description=\"Show an example curl command for using openai Completion API\", validators=[NoCodeSecrets(on_fail=\"fix\")])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -106,17 +148,47 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "import guardrails as gd\n", "\n", - "from rich import print\n", - "\n", + "from rich import print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the XML RAIL spec:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from the Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=ScrubbedCode, prompt=prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -127,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -141,23 +213,22 @@ "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "<output>\n", - " <string name=\"api_help\" description=\"Show an example curl command for using openai Completion API\" \n", - "format=\"no-code-secrets: \"/>\n", + " <string name=\"api_help\" format=\"no-code-secrets\" description=\"Show an example curl command for using openai \n", + "Completion API\"/>\n", "</output>\n", "\n", "\n", "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n", "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n", "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n", - "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n", + "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n", "\n", "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n", - "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n", - "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n", + "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n", + "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n", "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n", - "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n", + "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", "\n", - "JSON Object:\n", "\n", "\n" ], @@ -170,23 +241,22 @@ "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -207,15 +277,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] } ], @@ -238,7 +307,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -267,14 +336,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -285,8 +354,8 @@
        "    │ │ it into.                                                                                                │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ <output>                                                                                                │ │\n",
-       "    │ │     <string name=\"api_help\" description=\"Show an example curl command for using openai Completion API\"  │ │\n",
-       "    │ │ format=\"no-code-secrets: \"/>                                                                            │ │\n",
+       "    │ │     <string name=\"api_help\" format=\"no-code-secrets\" description=\"Show an example curl command for      │ │\n",
+       "    │ │ using openai Completion API\"/>                                                                          │ │\n",
        "    │ │ </output>                                                                                               │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -303,9 +372,18 @@
        "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ JSON Object:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ Json Output:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "    │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "    │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "    │ │ {\"api_help\": \"curl -X POST -H 'Content-Type: application/json' -d '{\\\"prompt\\\": \\\"The quick brown       │ │\n",
        "    │ │ fox\\\", \\\"max_tokens\\\": 10}' https://api.openai.com/v1/engines/completion/completions\"}                  │ │\n",
@@ -321,7 +399,7 @@
       ],
       "text/plain": [
        "Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -332,8 +410,8 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                         \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -350,9 +428,18 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"api_help\": \"curl -X POST -H 'Content-Type: application/json' -d '{\\\"prompt\\\": \\\"The quick brown \u001b[0m\u001b[48;2;245;245;220m     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mfox\\\", \\\"max_tokens\\\": 10}' https://api.openai.com/v1/engines/completion/completions\"}\u001b[0m\u001b[48;2;245;245;220m                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -391,7 +478,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.16"
+   "version": "3.11.4"
   },
   "orig_nbformat": 4,
   "vscode": {
diff --git a/docs/examples/recipe_generation.ipynb b/docs/examples/recipe_generation.ipynb
index b25ac93ee..2115a1fca 100644
--- a/docs/examples/recipe_generation.ipynb
+++ b/docs/examples/recipe_generation.ipynb
@@ -19,7 +19,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 14,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -34,27 +34,24 @@
    "source": [
     "## Step 1: Create the RAIL Spec\n",
     "\n",
-    "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n",
-    "\n",
-    "Here, we request:\n",
-    "\n"
+    "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).  We will also show the same RAIL spec in a code-first format using a Pydantic model."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "First we define a custom Validator:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [],
    "source": [
-    "rail_str = \"\"\"\n",
-    "\n",
-    "\n",
-    "\n",
-    "\n",
+    "        return PassResult()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Next we can define our RAIL spec either as a XML string:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "rail_str = \"\"\"\n",
+    "\n",
     "\n",
     "\n",
     "    \n",
@@ -110,13 +119,52 @@
     "\n",
     "\n",
     "Generate a recipe for vegan mac and cheese.\n",
-    "@complete_json_suffix\n",
+    "${gr.complete_json_suffix}\n",
     "\n",
     "\n",
     "\n",
     "\"\"\""
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "or a Pydantic model:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from pydantic import BaseModel, Field\n",
+    "from typing import List\n",
+    "\n",
+    "\n",
+    "prompt = \"\"\"\n",
+    "Generate a recipe for vegan mac and cheese.\n",
+    "${gr.complete_json_suffix}\n",
+    "\"\"\"\n",
+    "\n",
+    "class Ingredient(BaseModel):\n",
+    "    index: int = Field(validators=[(\"1-indexed\", \"noop\")])\n",
+    "    name: str = Field(validators=[IsVegan(on_fail=\"fix\")])\n",
+    "    brand: str = Field(description=\"Suggested brand for the ingredient (if any)\")\n",
+    "    optional: bool = Field(description=\"Is the ingredient necessary?\")\n",
+    "    quantity: float = Field(validators=[(\"units-imperial\", \"noop\")])\n",
+    "    units: str = Field(validators=[(\"units-imperial\", \"noop\")])\n",
+    "\n",
+    "class Instruction(BaseModel):\n",
+    "    index: int = Field(validators=[(\"1-indexed\", \"noop\")])\n",
+    "    step: str\n",
+    "\n",
+    "class Recipe(BaseModel):\n",
+    "    ingredients: List[Ingredient] = Field(description=\"What are the ingredients for the recipe?\")\n",
+    "    instructions: List[Instruction] = Field(description=\"What are the instructions for the recipe?\")"
+   ]
+  },
   {
    "attachments": {},
    "cell_type": "markdown",
@@ -140,20 +188,27 @@
     "3. Compiles the schema and type info from the RAIL spec and adds it to the prompt."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "From the XML string RAIL spec:"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
      "name": "stderr",
      "output_type": "stream",
      "text": [
-      "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator 1-indexed is not valid for element integer.\n",
+      "/home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages/guardrails/schema.py:218: UserWarning: Validator 1-indexed is not valid for element integer.\n",
       "  warnings.warn(\n",
-      "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator units-imperial is not valid for element float.\n",
+      "/home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages/guardrails/schema.py:218: UserWarning: Validator units-imperial is not valid for element float.\n",
       "  warnings.warn(\n",
-      "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator units-imperial is not valid for element string.\n",
+      "/home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages/guardrails/schema.py:218: UserWarning: Validator units-imperial is not valid for element string.\n",
       "  warnings.warn(\n"
      ]
     }
@@ -162,6 +217,22 @@
     "guard = gd.Guard.from_rail_string(rail_str)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "From the Pydantic model:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "guard = gd.Guard.from_pydantic(output_class=Recipe, prompt=prompt)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -173,7 +244,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 20,
    "metadata": {},
    "outputs": [
     {
@@ -207,15 +278,14 @@
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n",
        "
\n" ], @@ -248,15 +318,14 @@ "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -277,15 +346,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] } ], @@ -308,7 +376,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -316,7 +384,7 @@ "text/html": [ "
{\n",
        "    'ingredients': [\n",
-       "        {'index': 1, 'name': 'macaroni', 'brand': 'Barilla', 'optional': False, 'quantity': 8.0, 'units': 'oz'},\n",
+       "        {'index': 1, 'name': 'macaroni', 'brand': None, 'optional': False, 'quantity': 8.0, 'units': 'oz'},\n",
        "        {\n",
        "            'index': 2,\n",
        "            'name': 'vegan butter',\n",
@@ -328,46 +396,32 @@
        "        {\n",
        "            'index': 3,\n",
        "            'name': 'all-purpose flour',\n",
-       "            'brand': 'Gold Medal',\n",
+       "            'brand': None,\n",
        "            'optional': False,\n",
        "            'quantity': 2.0,\n",
        "            'units': 'tbsp'\n",
        "        },\n",
        "        {'index': 4, 'name': 'vegan milk', 'brand': 'Oatly', 'optional': False, 'quantity': 2.0, 'units': 'cups'},\n",
-       "        {'index': 5, 'name': 'vegan cheese', 'brand': 'Daiya', 'optional': False, 'quantity': 8.0, 'units': 'oz'},\n",
-       "        {\n",
-       "            'index': 6,\n",
-       "            'name': 'nutritional yeast',\n",
-       "            'brand': \"Bob's Red Mill\",\n",
-       "            'optional': False,\n",
-       "            'quantity': 2.0,\n",
-       "            'units': 'tbsp'\n",
-       "        },\n",
        "        {\n",
-       "            'index': 7,\n",
-       "            'name': 'garlic powder',\n",
-       "            'brand': 'McCormick',\n",
+       "            'index': 5,\n",
+       "            'name': 'vegan cheese',\n",
+       "            'brand': 'Daiya',\n",
        "            'optional': False,\n",
        "            'quantity': 1.0,\n",
-       "            'units': 'tsp'\n",
+       "            'units': 'cup'\n",
        "        },\n",
        "        {\n",
-       "            'index': 8,\n",
-       "            'name': 'onion powder',\n",
-       "            'brand': 'McCormick',\n",
+       "            'index': 6,\n",
+       "            'name': 'nutritional yeast',\n",
+       "            'brand': None,\n",
        "            'optional': False,\n",
-       "            'quantity': 1.0,\n",
-       "            'units': 'tsp'\n",
+       "            'quantity': 2.0,\n",
+       "            'units': 'tbsp'\n",
        "        },\n",
-       "        {'index': 9, 'name': 'salt', 'brand': 'Morton', 'optional': False, 'quantity': 1.0, 'units': 'tsp'},\n",
-       "        {\n",
-       "            'index': 10,\n",
-       "            'name': 'black pepper',\n",
-       "            'brand': 'McCormick',\n",
-       "            'optional': False,\n",
-       "            'quantity': 0.5,\n",
-       "            'units': 'tsp'\n",
-       "        }\n",
+       "        {'index': 7, 'name': 'garlic powder', 'brand': None, 'optional': False, 'quantity': 1.0, 'units': 'tsp'},\n",
+       "        {'index': 8, 'name': 'onion powder', 'brand': None, 'optional': False, 'quantity': 1.0, 'units': 'tsp'},\n",
+       "        {'index': 9, 'name': 'salt', 'brand': None, 'optional': False, 'quantity': 0.5, 'units': 'tsp'},\n",
+       "        {'index': 10, 'name': 'black pepper', 'brand': None, 'optional': False, 'quantity': 0.5, 'units': 'tsp'}\n",
        "    ],\n",
        "    'instructions': [\n",
        "        {\n",
@@ -375,23 +429,26 @@
        "            'step': 'Bring a large pot of salted water to a boil. Add the macaroni and cook according to package \n",
        "instructions.'\n",
        "        },\n",
-       "        {'index': 2, 'step': 'Drain the macaroni and set aside.'},\n",
+       "        {\n",
+       "            'index': 2,\n",
+       "            'step': 'Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the flour and \n",
+       "whisk until combined. Cook for 1 minute, stirring constantly.'\n",
+       "        },\n",
        "        {\n",
        "            'index': 3,\n",
-       "            'step': 'In a medium saucepan, melt the vegan butter over medium heat. Add the flour and whisk until \n",
-       "combined and bubbly.'\n",
+       "            'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook for 3-4 \n",
+       "minutes, stirring constantly, until the mixture is thickened.'\n",
        "        },\n",
        "        {\n",
        "            'index': 4,\n",
-       "            'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth and thickened.'\n",
+       "            'step': 'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and pepper. Stir \n",
+       "until the cheese is melted and the sauce is smooth.'\n",
        "        },\n",
        "        {\n",
        "            'index': 5,\n",
-       "            'step': 'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and pepper. Stir \n",
-       "until the cheese is melted and the sauce is smooth.'\n",
+       "            'step': 'Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly coated.'\n",
        "        },\n",
-       "        {'index': 6, 'step': 'Add the cooked macaroni to the sauce and stir until combined.'},\n",
-       "        {'index': 7, 'step': 'Serve the vegan mac and cheese warm.'}\n",
+       "        {'index': 6, 'step': 'Serve the vegan mac and cheese warm.'}\n",
        "    ]\n",
        "}\n",
        "
\n" @@ -399,7 +456,7 @@ "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m'ingredients'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'macaroni'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m'Barilla'\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m8.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'oz'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'macaroni'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m8.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'oz'\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'vegan butter'\u001b[0m,\n", @@ -411,46 +468,32 @@ " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'all-purpose flour'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'Gold Medal'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m,\n", " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m2.0\u001b[0m,\n", " \u001b[32m'units'\u001b[0m: \u001b[32m'tbsp'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'vegan milk'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m'Oatly'\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m2.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'cups'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'vegan cheese'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m'Daiya'\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m8.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'oz'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'nutritional yeast'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m\"Bob's Red Mill\"\u001b[0m,\n", - " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", - " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m2.0\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'tbsp'\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'garlic powder'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'McCormick'\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'vegan cheese'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[32m'Daiya'\u001b[0m,\n", " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\n", + " \u001b[32m'units'\u001b[0m: \u001b[32m'cup'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'onion powder'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'McCormick'\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'nutritional yeast'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m,\n", " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", - " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\n", + " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m2.0\u001b[0m,\n", + " \u001b[32m'units'\u001b[0m: \u001b[32m'tbsp'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'salt'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m'Morton'\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m10\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'black pepper'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'McCormick'\u001b[0m,\n", - " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", - " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.5\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\n", - " \u001b[1m}\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'garlic powder'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'onion powder'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m9\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'salt'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.5\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m10\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'black pepper'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[3;35mNone\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.5\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'tsp'\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m,\n", " \u001b[32m'instructions'\u001b[0m: \u001b[1m[\u001b[0m\n", " \u001b[1m{\u001b[0m\n", @@ -458,23 +501,26 @@ " \u001b[32m'step'\u001b[0m: \u001b[32m'Bring a large pot of salted water to a boil. Add the macaroni and cook according to package \u001b[0m\n", "\u001b[32minstructions.'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Drain the macaroni and set aside.'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", + " \u001b[32m'step'\u001b[0m: \u001b[32m'Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the flour and \u001b[0m\n", + "\u001b[32mwhisk until combined. Cook for 1 minute, stirring constantly.'\u001b[0m\n", + " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", - " \u001b[32m'step'\u001b[0m: \u001b[32m'In a medium saucepan, melt the vegan butter over medium heat. Add the flour and whisk until \u001b[0m\n", - "\u001b[32mcombined and bubbly.'\u001b[0m\n", + " \u001b[32m'step'\u001b[0m: \u001b[32m'Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook for 3-4 \u001b[0m\n", + "\u001b[32mminutes, stirring constantly, until the mixture is thickened.'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m,\n", - " \u001b[32m'step'\u001b[0m: \u001b[32m'Slowly add the vegan milk, whisking constantly until the mixture is smooth and thickened.'\u001b[0m\n", + " \u001b[32m'step'\u001b[0m: \u001b[32m'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and pepper. Stir \u001b[0m\n", + "\u001b[32muntil the cheese is melted and the sauce is smooth.'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", - " \u001b[32m'step'\u001b[0m: \u001b[32m'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and pepper. Stir \u001b[0m\n", - "\u001b[32muntil the cheese is melted and the sauce is smooth.'\u001b[0m\n", + " \u001b[32m'step'\u001b[0m: \u001b[32m'Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly coated.'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Add the cooked macaroni to the sauce and stir until combined.'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m7\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Serve the vegan mac and cheese warm.'\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m6\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Serve the vegan mac and cheese warm.'\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -489,14 +535,14 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ Generate a recipe for vegan mac and cheese.                                                             │ │\n",
@@ -537,16 +583,25 @@
        "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ JSON Object:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ Json Output:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "    │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "    │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "    │ │ {                                                                                                       │ │\n",
        "    │ │     \"ingredients\": [                                                                                    │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 1,                                                                                 │ │\n",
        "    │ │             \"name\": \"macaroni\",                                                                         │ │\n",
-       "    │ │             \"brand\": \"Barilla\",                                                                         │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 8.0,                                                                            │ │\n",
        "    │ │             \"units\": \"oz\"                                                                               │ │\n",
@@ -562,7 +617,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 3,                                                                                 │ │\n",
        "    │ │             \"name\": \"all-purpose flour\",                                                                │ │\n",
-       "    │ │             \"brand\": \"Gold Medal\",                                                                      │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 2.0,                                                                            │ │\n",
        "    │ │             \"units\": \"tbsp\"                                                                             │ │\n",
@@ -580,13 +635,13 @@
        "    │ │             \"name\": \"vegan cheese\",                                                                     │ │\n",
        "    │ │             \"brand\": \"Daiya\",                                                                           │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
-       "    │ │             \"quantity\": 8.0,                                                                            │ │\n",
-       "    │ │             \"units\": \"oz\"                                                                               │ │\n",
+       "    │ │             \"quantity\": 1.0,                                                                            │ │\n",
+       "    │ │             \"units\": \"cup\"                                                                              │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 6,                                                                                 │ │\n",
        "    │ │             \"name\": \"nutritional yeast\",                                                                │ │\n",
-       "    │ │             \"brand\": \"Bob's Red Mill\",                                                                  │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 2.0,                                                                            │ │\n",
        "    │ │             \"units\": \"tbsp\"                                                                             │ │\n",
@@ -594,7 +649,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 7,                                                                                 │ │\n",
        "    │ │             \"name\": \"garlic powder\",                                                                    │ │\n",
-       "    │ │             \"brand\": \"McCormick\",                                                                       │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 1.0,                                                                            │ │\n",
        "    │ │             \"units\": \"tsp\"                                                                              │ │\n",
@@ -602,7 +657,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 8,                                                                                 │ │\n",
        "    │ │             \"name\": \"onion powder\",                                                                     │ │\n",
-       "    │ │             \"brand\": \"McCormick\",                                                                       │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 1.0,                                                                            │ │\n",
        "    │ │             \"units\": \"tsp\"                                                                              │ │\n",
@@ -610,15 +665,15 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 9,                                                                                 │ │\n",
        "    │ │             \"name\": \"salt\",                                                                             │ │\n",
-       "    │ │             \"brand\": \"Morton\",                                                                          │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
-       "    │ │             \"quantity\": 1.0,                                                                            │ │\n",
+       "    │ │             \"quantity\": 0.5,                                                                            │ │\n",
        "    │ │             \"units\": \"tsp\"                                                                              │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 10,                                                                                │ │\n",
        "    │ │             \"name\": \"black pepper\",                                                                     │ │\n",
-       "    │ │             \"brand\": \"McCormick\",                                                                       │ │\n",
+       "    │ │             \"brand\": null,                                                                              │ │\n",
        "    │ │             \"optional\": false,                                                                          │ │\n",
        "    │ │             \"quantity\": 0.5,                                                                            │ │\n",
        "    │ │             \"units\": \"tsp\"                                                                              │ │\n",
@@ -632,29 +687,26 @@
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 2,                                                                                 │ │\n",
-       "    │ │             \"step\": \"Drain the macaroni and set aside.\"                                                 │ │\n",
+       "    │ │             \"step\": \"Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the    │ │\n",
+       "    │ │ flour and whisk until combined. Cook for 1 minute, stirring constantly.\"                                │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 3,                                                                                 │ │\n",
-       "    │ │             \"step\": \"In a medium saucepan, melt the vegan butter over medium heat. Add the flour and    │ │\n",
-       "    │ │ whisk until combined and bubbly.\"                                                                       │ │\n",
+       "    │ │             \"step\": \"Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook   │ │\n",
+       "    │ │ for 3-4 minutes, stirring constantly, until the mixture is thickened.\"                                  │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             \"index\": 4,                                                                                 │ │\n",
-       "    │ │             \"step\": \"Slowly add the vegan milk, whisking constantly until the mixture is smooth and     │ │\n",
-       "    │ │ thickened.\"                                                                                             │ │\n",
-       "    │ │         },                                                                                              │ │\n",
-       "    │ │         {                                                                                               │ │\n",
-       "    │ │             \"index\": 5,                                                                                 │ │\n",
        "    │ │             \"step\": \"Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and    │ │\n",
        "    │ │ pepper. Stir until the cheese is melted and the sauce is smooth.\"                                       │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             \"index\": 6,                                                                                 │ │\n",
-       "    │ │             \"step\": \"Add the cooked macaroni to the sauce and stir until combined.\"                     │ │\n",
+       "    │ │             \"index\": 5,                                                                                 │ │\n",
+       "    │ │             \"step\": \"Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly      │ │\n",
+       "    │ │ coated.\"                                                                                                │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             \"index\": 7,                                                                                 │ │\n",
+       "    │ │             \"index\": 6,                                                                                 │ │\n",
        "    │ │             \"step\": \"Serve the vegan mac and cheese warm.\"                                              │ │\n",
        "    │ │         }                                                                                               │ │\n",
        "    │ │     ]                                                                                                   │ │\n",
@@ -666,7 +718,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 1,                                                                                 │ │\n",
        "    │ │             'name': 'macaroni',                                                                         │ │\n",
-       "    │ │             'brand': 'Barilla',                                                                         │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 8.0,                                                                            │ │\n",
        "    │ │             'units': 'oz'                                                                               │ │\n",
@@ -682,7 +734,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 3,                                                                                 │ │\n",
        "    │ │             'name': 'all-purpose flour',                                                                │ │\n",
-       "    │ │             'brand': 'Gold Medal',                                                                      │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 2.0,                                                                            │ │\n",
        "    │ │             'units': 'tbsp'                                                                             │ │\n",
@@ -700,13 +752,13 @@
        "    │ │             'name': 'vegan cheese',                                                                     │ │\n",
        "    │ │             'brand': 'Daiya',                                                                           │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
-       "    │ │             'quantity': 8.0,                                                                            │ │\n",
-       "    │ │             'units': 'oz'                                                                               │ │\n",
+       "    │ │             'quantity': 1.0,                                                                            │ │\n",
+       "    │ │             'units': 'cup'                                                                              │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 6,                                                                                 │ │\n",
        "    │ │             'name': 'nutritional yeast',                                                                │ │\n",
-       "    │ │             'brand': \"Bob's Red Mill\",                                                                  │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 2.0,                                                                            │ │\n",
        "    │ │             'units': 'tbsp'                                                                             │ │\n",
@@ -714,7 +766,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 7,                                                                                 │ │\n",
        "    │ │             'name': 'garlic powder',                                                                    │ │\n",
-       "    │ │             'brand': 'McCormick',                                                                       │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 1.0,                                                                            │ │\n",
        "    │ │             'units': 'tsp'                                                                              │ │\n",
@@ -722,7 +774,7 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 8,                                                                                 │ │\n",
        "    │ │             'name': 'onion powder',                                                                     │ │\n",
-       "    │ │             'brand': 'McCormick',                                                                       │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 1.0,                                                                            │ │\n",
        "    │ │             'units': 'tsp'                                                                              │ │\n",
@@ -730,15 +782,15 @@
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 9,                                                                                 │ │\n",
        "    │ │             'name': 'salt',                                                                             │ │\n",
-       "    │ │             'brand': 'Morton',                                                                          │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
-       "    │ │             'quantity': 1.0,                                                                            │ │\n",
+       "    │ │             'quantity': 0.5,                                                                            │ │\n",
        "    │ │             'units': 'tsp'                                                                              │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'index': 10,                                                                                │ │\n",
        "    │ │             'name': 'black pepper',                                                                     │ │\n",
-       "    │ │             'brand': 'McCormick',                                                                       │ │\n",
+       "    │ │             'brand': None,                                                                              │ │\n",
        "    │ │             'optional': False,                                                                          │ │\n",
        "    │ │             'quantity': 0.5,                                                                            │ │\n",
        "    │ │             'units': 'tsp'                                                                              │ │\n",
@@ -750,27 +802,27 @@
        "    │ │             'step': 'Bring a large pot of salted water to a boil. Add the macaroni and cook according   │ │\n",
        "    │ │ to package instructions.'                                                                               │ │\n",
        "    │ │         },                                                                                              │ │\n",
-       "    │ │         {'index': 2, 'step': 'Drain the macaroni and set aside.'},                                      │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 3,                                                                                 │ │\n",
-       "    │ │             'step': 'In a medium saucepan, melt the vegan butter over medium heat. Add the flour and    │ │\n",
-       "    │ │ whisk until combined and bubbly.'                                                                       │ │\n",
+       "    │ │             'index': 2,                                                                                 │ │\n",
+       "    │ │             'step': 'Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the    │ │\n",
+       "    │ │ flour and whisk until combined. Cook for 1 minute, stirring constantly.'                                │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 4,                                                                                 │ │\n",
-       "    │ │             'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth and     │ │\n",
-       "    │ │ thickened.'                                                                                             │ │\n",
+       "    │ │             'index': 3,                                                                                 │ │\n",
+       "    │ │             'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook   │ │\n",
+       "    │ │ for 3-4 minutes, stirring constantly, until the mixture is thickened.'                                  │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 5,                                                                                 │ │\n",
+       "    │ │             'index': 4,                                                                                 │ │\n",
        "    │ │             'step': 'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and    │ │\n",
        "    │ │ pepper. Stir until the cheese is melted and the sauce is smooth.'                                       │ │\n",
        "    │ │         },                                                                                              │ │\n",
        "    │ │         {                                                                                               │ │\n",
-       "    │ │             'index': 6,                                                                                 │ │\n",
-       "    │ │             'step': 'Add the cooked macaroni to the sauce and stir until combined.'                     │ │\n",
+       "    │ │             'index': 5,                                                                                 │ │\n",
+       "    │ │             'step': 'Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly      │ │\n",
+       "    │ │ coated.'                                                                                                │ │\n",
        "    │ │         },                                                                                              │ │\n",
-       "    │ │         {'index': 7, 'step': 'Serve the vegan mac and cheese warm.'}                                    │ │\n",
+       "    │ │         {'index': 6, 'step': 'Serve the vegan mac and cheese warm.'}                                    │ │\n",
        "    │ │     ]                                                                                                   │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
@@ -779,7 +831,7 @@
       ],
       "text/plain": [
        "Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mGenerate a recipe for vegan mac and cheese.\u001b[0m\u001b[48;2;240;248;255m                                                            \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -820,16 +872,25 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m                                                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    \"ingredients\": [\u001b[0m\u001b[48;2;245;245;220m                                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 1,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"macaroni\",\u001b[0m\u001b[48;2;245;245;220m                                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"Barilla\",\u001b[0m\u001b[48;2;245;245;220m                                                                        \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 8.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"oz\"\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -845,7 +906,7 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"all-purpose flour\",\u001b[0m\u001b[48;2;245;245;220m                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"Gold Medal\",\u001b[0m\u001b[48;2;245;245;220m                                                                     \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 2.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tbsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -863,13 +924,13 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"vegan cheese\",\u001b[0m\u001b[48;2;245;245;220m                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"Daiya\",\u001b[0m\u001b[48;2;245;245;220m                                                                          \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 8.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"oz\"\u001b[0m\u001b[48;2;245;245;220m                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 1.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"cup\"\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"nutritional yeast\",\u001b[0m\u001b[48;2;245;245;220m                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"Bob's Red Mill\",\u001b[0m\u001b[48;2;245;245;220m                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 2.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tbsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -877,7 +938,7 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"garlic powder\",\u001b[0m\u001b[48;2;245;245;220m                                                                   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"McCormick\",\u001b[0m\u001b[48;2;245;245;220m                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 1.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -885,7 +946,7 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 8,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"onion powder\",\u001b[0m\u001b[48;2;245;245;220m                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"McCormick\",\u001b[0m\u001b[48;2;245;245;220m                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 1.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -893,15 +954,15 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 9,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"salt\",\u001b[0m\u001b[48;2;245;245;220m                                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"Morton\",\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 1.0,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 0.5,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 10,\u001b[0m\u001b[48;2;245;245;220m                                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"name\": \"black pepper\",\u001b[0m\u001b[48;2;245;245;220m                                                                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": \"McCormick\",\u001b[0m\u001b[48;2;245;245;220m                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"brand\": null,\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"optional\": false,\u001b[0m\u001b[48;2;245;245;220m                                                                         \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"quantity\": 0.5,\u001b[0m\u001b[48;2;245;245;220m                                                                           \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"units\": \"tsp\"\u001b[0m\u001b[48;2;245;245;220m                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -915,29 +976,26 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 2,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Drain the macaroni and set aside.\"\u001b[0m\u001b[48;2;245;245;220m                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mflour and whisk until combined. Cook for 1 minute, stirring constantly.\"\u001b[0m\u001b[48;2;245;245;220m                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 3,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"In a medium saucepan, melt the vegan butter over medium heat. Add the flour and \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mwhisk until combined and bubbly.\"\u001b[0m\u001b[48;2;245;245;220m                                                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mfor 3-4 minutes, stirring constantly, until the mixture is thickened.\"\u001b[0m\u001b[48;2;245;245;220m                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 4,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Slowly add the vegan milk, whisking constantly until the mixture is smooth and \u001b[0m\u001b[48;2;245;245;220m   \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mthickened.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mpepper. Stir until the cheese is melted and the sauce is smooth.\"\u001b[0m\u001b[48;2;245;245;220m                                      \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Add the cooked macaroni to the sauce and stir until combined.\"\u001b[0m\u001b[48;2;245;245;220m                    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 5,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly \u001b[0m\u001b[48;2;245;245;220m    \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mcoated.\"\u001b[0m\u001b[48;2;245;245;220m                                                                                               \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 7,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"index\": 6,\u001b[0m\u001b[48;2;245;245;220m                                                                                \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"step\": \"Serve the vegan mac and cheese warm.\"\u001b[0m\u001b[48;2;245;245;220m                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m        }\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    ]\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
@@ -949,7 +1007,7 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 1,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'macaroni',\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'Barilla',\u001b[0m\u001b[48;2;240;255;240m                                                                        \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 8.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'oz'\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -965,7 +1023,7 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 3,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'all-purpose flour',\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'Gold Medal',\u001b[0m\u001b[48;2;240;255;240m                                                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 2.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tbsp'\u001b[0m\u001b[48;2;240;255;240m                                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -983,13 +1041,13 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'vegan cheese',\u001b[0m\u001b[48;2;240;255;240m                                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'Daiya',\u001b[0m\u001b[48;2;240;255;240m                                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 8.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'oz'\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 1.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'cup'\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 6,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'nutritional yeast',\u001b[0m\u001b[48;2;240;255;240m                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': \"Bob's Red Mill\",\u001b[0m\u001b[48;2;240;255;240m                                                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 2.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tbsp'\u001b[0m\u001b[48;2;240;255;240m                                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -997,7 +1055,7 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 7,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'garlic powder',\u001b[0m\u001b[48;2;240;255;240m                                                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'McCormick',\u001b[0m\u001b[48;2;240;255;240m                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 1.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tsp'\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1005,7 +1063,7 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 8,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'onion powder',\u001b[0m\u001b[48;2;240;255;240m                                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'McCormick',\u001b[0m\u001b[48;2;240;255;240m                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 1.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tsp'\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1013,15 +1071,15 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 9,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'salt',\u001b[0m\u001b[48;2;240;255;240m                                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'Morton',\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 1.0,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 0.5,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tsp'\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 10,\u001b[0m\u001b[48;2;240;255;240m                                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'name': 'black pepper',\u001b[0m\u001b[48;2;240;255;240m                                                                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': 'McCormick',\u001b[0m\u001b[48;2;240;255;240m                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'brand': None,\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'optional': False,\u001b[0m\u001b[48;2;240;255;240m                                                                         \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'quantity': 0.5,\u001b[0m\u001b[48;2;240;255;240m                                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'units': 'tsp'\u001b[0m\u001b[48;2;240;255;240m                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1033,27 +1091,27 @@
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Bring a large pot of salted water to a boil. Add the macaroni and cook according \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mto package instructions.'\u001b[0m\u001b[48;2;240;255;240m                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {'index': 2, 'step': 'Drain the macaroni and set aside.'},\u001b[0m\u001b[48;2;240;255;240m                                     \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 3,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'In a medium saucepan, melt the vegan butter over medium heat. Add the flour and \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwhisk until combined and bubbly.'\u001b[0m\u001b[48;2;240;255;240m                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 2,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Meanwhile, melt the vegan butter in a medium saucepan over medium heat. Add the \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mflour and whisk until combined. Cook for 1 minute, stirring constantly.'\u001b[0m\u001b[48;2;240;255;240m                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 4,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth and \u001b[0m\u001b[48;2;240;255;240m   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mthickened.'\u001b[0m\u001b[48;2;240;255;240m                                                                                            \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 3,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Slowly add the vegan milk, whisking constantly until the mixture is smooth. Cook \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mfor 3-4 minutes, stirring constantly, until the mixture is thickened.'\u001b[0m\u001b[48;2;240;255;240m                                 \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 4,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Add the vegan cheese, nutritional yeast, garlic powder, onion powder, salt, and \u001b[0m\u001b[48;2;240;255;240m  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mpepper. Stir until the cheese is melted and the sauce is smooth.'\u001b[0m\u001b[48;2;240;255;240m                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 6,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Add the cooked macaroni to the sauce and stir until combined.'\u001b[0m\u001b[48;2;240;255;240m                    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'index': 5,\u001b[0m\u001b[48;2;240;255;240m                                                                                \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m            'step': 'Drain the macaroni and add it to the sauce. Stir until the macaroni is evenly \u001b[0m\u001b[48;2;240;255;240m    \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mcoated.'\u001b[0m\u001b[48;2;240;255;240m                                                                                               \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        },\u001b[0m\u001b[48;2;240;255;240m                                                                                             \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {'index': 7, 'step': 'Serve the vegan mac and cheese warm.'}\u001b[0m\u001b[48;2;240;255;240m                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {'index': 6, 'step': 'Serve the vegan mac and cheese warm.'}\u001b[0m\u001b[48;2;240;255;240m                                   \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m    ]\u001b[0m\u001b[48;2;240;255;240m                                                                                                  \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m                                                                                                      \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
@@ -1085,7 +1143,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.16"
+   "version": "3.11.3"
   },
   "orig_nbformat": 4,
   "vscode": {
diff --git a/docs/examples/select_choice_based_on_action.ipynb b/docs/examples/select_choice_based_on_action.ipynb
index 963e8ff97..05fa94083 100644
--- a/docs/examples/select_choice_based_on_action.ipynb
+++ b/docs/examples/select_choice_based_on_action.ipynb
@@ -1,7 +1,6 @@
 {
  "cells": [
   {
-   "attachments": {},
    "cell_type": "markdown",
    "metadata": {},
    "source": [
@@ -19,33 +18,43 @@
     "\n",
     "## Objective\n",
     "\n",
-    "We want the LLM to play an RP game where it can choose to either `fight` or `flight`. If it chooses to `fight`, the LLM should choose a `weapon` and an `enemy`. If the player chooses `flight`, the LLM shoudl choose a `direction` and a `distance`.\n",
+    "We want the LLM to play an RP game where it can choose to either `fight` or `flight`. If it chooses to `fight`, the LLM should choose a `weapon` and an `enemy`. If the player chooses `flight`, the LLM should choose a `direction` and a `distance`.\n",
     "\n",
     "\n",
     "## Step 1: Generating `RAIL` Spec\n",
     "\n",
-    "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string."
+    "Ordinarily, we could create a separate `RAIL` spec in a file. However, for the sake of this example, we will generate the `RAIL` spec in the notebook as a string or a Pydantic Model."
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": 1,
+   "cell_type": "markdown",
    "metadata": {},
+   "source": [
+    "XML option:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {
+    "ExecuteTime": {
+     "end_time": "2023-08-23T15:09:26.331177Z",
+     "start_time": "2023-08-23T15:09:26.327325Z"
+    }
+   },
    "outputs": [],
    "source": [
     "rail_str = \"\"\"\n",
     "\n",
     "\n",
     "\n",
-    "    \n",
+    "    \n",
     "        \n",
     "            \n",
     "        \n",
     "        \n",
-    "            \n",
-    "                \n",
-    "                \n",
-    "            \n",
+    "            \n",
+    "            \n",
     "        \n",
     "    \n",
     "\n",
@@ -53,14 +62,52 @@
     "\n",
     "You are a human in an enchanted forest. You come across opponents of different types, and you should fight smaller opponents and run away from bigger ones.\n",
     "\n",
-    "You run into a {{opp_type}}. What do you do?\n",
+    "You run into a ${opp_type}. What do you do?\n",
     "\n",
-    "@complete_json_suffix_v2\n",
+    "${gr.complete_json_suffix_v2}\n",
     "\n",
     "\n",
     "\"\"\""
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Pydantic model option:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from guardrails.validators import ValidChoices\n",
+    "from pydantic import BaseModel, Field\n",
+    "from typing import Literal, Union\n",
+    "\n",
+    "prompt = \"\"\"\n",
+    "You are a human in an enchanted forest. You come across opponents of different types, and you should fight smaller opponents and run away from bigger ones.\n",
+    "\n",
+    "You run into a ${opp_type}. What do you do?\n",
+    "\n",
+    "${gr.complete_json_suffix_v2}\"\"\"\n",
+    "\n",
+    "class Fight(BaseModel):\n",
+    "    chosen_action: Literal['fight']\n",
+    "    weapon: str = Field(validators=[ValidChoices(['crossbow', 'machine gun'], on_fail=\"reask\")])\n",
+    "\n",
+    "class Flight(BaseModel):\n",
+    "    chosen_action: Literal['flight']\n",
+    "    flight_direction: str = Field(validators=[ValidChoices(['north','south','east','west'], on_fail=\"exception\")])\n",
+    "    distance: int = Field(validators=[ValidChoices([1,2,3,4], on_fail=\"exception\")])\n",
+    "\n",
+    "class FightOrFlight(BaseModel):\n",
+    "    action: Union[Fight, Flight] = Field(discriminator='chosen_action')\n",
+    "    "
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -75,9 +122,21 @@
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": 3,
+   "cell_type": "markdown",
    "metadata": {},
+   "source": [
+    "From XML:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {
+    "ExecuteTime": {
+     "end_time": "2023-08-23T15:09:28.590929Z",
+     "start_time": "2023-08-23T15:09:27.966099Z"
+    }
+   },
    "outputs": [],
    "source": [
     "import guardrails as gd\n",
@@ -91,13 +150,38 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "The `Guard` object compiles the output schema and adds it to the prompt. We can see the final prompt below:"
+    "Or from Pydantic:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 21,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import guardrails as gd\n",
+    "\n",
+    "from rich import print\n",
+    "\n",
+    "guard = gd.Guard.from_pydantic(output_class=FightOrFlight, prompt=prompt)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
    "metadata": {},
+   "source": [
+    "The `Guard` object compiles the output schema and adds it to the prompt. We can see the final prompt below:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {
+    "ExecuteTime": {
+     "end_time": "2023-08-23T15:09:32.364711Z",
+     "start_time": "2023-08-23T15:09:32.356959Z"
+    }
+   },
    "outputs": [
     {
      "data": {
@@ -106,18 +190,21 @@
        "You are a human in an enchanted forest. You come across opponents of different types, and you should fight smaller \n",
        "opponents and run away from bigger ones.\n",
        "\n",
-       "You run into a {opp_type}. What do you do?\n",
+       "You run into a ${opp_type}. What do you do?\n",
        "\n",
        "\n",
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "<output>\n",
-       "    <string name=\"action\" choices=\"fight,flight\"/>\n",
-       "    <string format=\"valid-choices: choices=['crossbow', 'machine gun']\" name=\"fight\" if=\"action==fight\"/>\n",
-       "    <object name=\"flight\" if=\"action==flight\">\n",
-       "        <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', 'west']\"/>\n",
-       "        <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/>\n",
-       "    </object>\n",
+       "    <choice name=\"action\" discriminator=\"chosen_action\">\n",
+       "        <case name=\"fight\">\n",
+       "            <string name=\"weapon\" format=\"valid-choices: choices=['crossbow', 'machine gun']\"/>\n",
+       "        </case>\n",
+       "        <case name=\"flight\">\n",
+       "            <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', 'west']\"/>\n",
+       "            <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/>\n",
+       "        </case>\n",
+       "    </choice>\n",
        "</output>\n",
        "\n",
        "\n",
@@ -127,15 +214,11 @@
        "specific types. Be correct and concise.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
-       "- `<string name=\"foo\" choices=\"bar,baz\"/><case name=\"bar\" on=\"foo\"><string format=\"valid-choices: ['a', \n",
-       "'b']\"/></case><case name=\"baz\" on=\"foo\"><object><string name=\"qux\" format=\"valid-choices: ['corge', \n",
-       "'grault']\"/><integer name=\"quux\" format=\"valid-choices: [1, 2, 3, 4]\"/></object></case>` => `{{'foo': 'baz', 'baz':\n",
-       "{{'qux': 'corge', 'quux': 1}}}}`\n",
-       "JSON Object:\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
+       "\n",
        "
\n" ], "text/plain": [ @@ -143,18 +226,21 @@ "You are a human in an enchanted forest. You come across opponents of different types, and you should fight smaller \n", "opponents and run away from bigger ones.\n", "\n", - "You run into a \u001b[1m{\u001b[0mopp_type\u001b[1m}\u001b[0m. What do you do?\n", + "You run into a $\u001b[1m{\u001b[0mopp_type\u001b[1m}\u001b[0m. What do you do?\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mcase\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mcase\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mchoice\u001b[0m\u001b[39m>\u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", @@ -164,15 +250,11 @@ "\u001b[39mspecific types. Be correct and concise.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'baz'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'Some String'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'index'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mcase\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mcase\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'baz'\u001b[0m, \u001b[32m'baz'\u001b[0m:\n", - "\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'qux'\u001b[0m: \u001b[32m'corge'\u001b[0m, \u001b[32m'quux'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", - "JSON Object:\n" + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\n" ] }, "metadata": {}, @@ -191,34 +273,45 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "We can now wrap the LsLM API call with the `Guard` object. This will ensure that the LLM generates an output that is compliant with the RAIL spec.\n", + "We can now wrap the LLM API call with the `Guard` object. This will ensure that the LLM generates an output that is compliant with the RAIL spec.\n", "\n", "To start, we test with a 'giant' as an opponent, and look at the output." ] }, { "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [], + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2023-08-23T15:10:08.998121Z", + "start_time": "2023-08-23T15:10:08.792027Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "import openai\n", "\n", "raw_llm_response, validated_response = guard(\n", - " openai.Completion.create,\n", + " openai.ChatCompletion.create,\n", " prompt_params={'opp_type': 'giant'},\n", - " engine=\"text-davinci-003\",\n", + " model=\"gpt-3.5-turbo\",\n", " max_tokens=256,\n", " temperature=0.0,\n", ")" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -231,17 +324,17 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
{'action': 'flight', 'flight': {'flight_direction': 'north', 'distance': 1}}\n",
+       "
{'action': {'chosen_action': 'flight', 'flight_direction': 'north', 'distance': 1}}\n",
        "
\n" ], "text/plain": [ - "\u001b[1m{\u001b[0m\u001b[32m'action'\u001b[0m: \u001b[32m'flight'\u001b[0m, \u001b[32m'flight'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'flight_direction'\u001b[0m: \u001b[32m'north'\u001b[0m, \u001b[32m'distance'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n" + "\u001b[1m{\u001b[0m\u001b[32m'action'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'chosen_action'\u001b[0m: \u001b[32m'flight'\u001b[0m, \u001b[32m'flight_direction'\u001b[0m: \u001b[32m'north'\u001b[0m, \u001b[32m'distance'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -253,7 +346,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -262,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -282,14 +374,16 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"action\" choices=\"fight,flight\"/> │ │\n", - " │ │ <string format=\"valid-choices: choices=['crossbow', 'machine gun']\" name=\"fight\" │ │\n", - " │ │ if=\"action==fight\"/> │ │\n", - " │ │ <object name=\"flight\" if=\"action==flight\"> │ │\n", - " │ │ <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', │ │\n", + " │ │ <choice name=\"action\" discriminator=\"chosen_action\"> │ │\n", + " │ │ <case name=\"fight\"> │ │\n", + " │ │ <string name=\"weapon\" format=\"valid-choices: choices=['crossbow', 'machine gun']\"/> │ │\n", + " │ │ </case> │ │\n", + " │ │ <case name=\"flight\"> │ │\n", + " │ │ <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', │ │\n", " │ │ 'west']\"/> │ │\n", - " │ │ <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/> │ │\n", - " │ │ </object> │ │\n", + " │ │ <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/> │ │\n", + " │ │ </case> │ │\n", + " │ │ </choice> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -304,18 +398,35 @@ " │ │ etc.]}` │ │\n", " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", - " │ │ - `<string name=\"foo\" choices=\"bar,baz\"/><case name=\"bar\" on=\"foo\"><string format=\"valid-choices: ['a', │ │\n", - " │ │ 'b']\"/></case><case name=\"baz\" on=\"foo\"><object><string name=\"qux\" format=\"valid-choices: ['corge', │ │\n", - " │ │ 'grault']\"/><integer name=\"quux\" format=\"valid-choices: [1, 2, 3, 4]\"/></object></case>` => `{'foo': │ │\n", - " │ │ 'baz', 'baz': {'qux': 'corge', 'quux': 1}}` │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n", + " │ │ You are a helpful assistant, able to express yourself purely through JSON, strictly and precisely │ │\n", + " │ │ adhering to the provided XML schemas. │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ │ │\n", - " │ │ {\"action\": \"flight\", \"flight\": {\"flight_direction\": \"north\", \"distance\": 1}} │ │\n", + " │ │ { │ │\n", + " │ │ \"action\": { │ │\n", + " │ │ \"chosen_action\": \"flight\", │ │\n", + " │ │ \"flight_direction\": \"north\", │ │\n", + " │ │ \"distance\": 1 │ │\n", + " │ │ } │ │\n", + " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ {'action': 'flight', 'flight': {'flight_direction': 'north', 'distance': 1}} │ │\n", + " │ │ { │ │\n", + " │ │ 'action': { │ │\n", + " │ │ 'chosen_action': 'flight', │ │\n", + " │ │ 'flight_direction': 'north', │ │\n", + " │ │ 'distance': 1 │ │\n", + " │ │ } │ │\n", + " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "
\n" @@ -335,14 +446,16 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -357,18 +470,35 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255metc.]}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'foo': \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m'baz', 'baz': {'qux': 'corge', 'quux': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant, able to express yourself purely through JSON, strictly and precisely \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242madhering to the provided XML schemas.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"action\": \"flight\", \"flight\": {\"flight_direction\": \"north\", \"distance\": 1}}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"action\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"chosen_action\": \"flight\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"flight_direction\": \"north\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"distance\": 1\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{'action': 'flight', 'flight': {'flight_direction': 'north', 'distance': 1}}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'action': {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'chosen_action': 'flight',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'flight_direction': 'north',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'distance': 1\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] @@ -382,7 +512,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -393,14 +522,22 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 26, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "raw_llm_response, validated_response = guard(\n", - " openai.Completion.create,\n", + " openai.ChatCompletion.create,\n", " prompt_params={'opp_type': 'goblin'},\n", - " engine=\"text-davinci-003\",\n", + " model=\"gpt-3.5-turbo\",\n", " max_tokens=256,\n", " temperature=0.0,\n", ")" @@ -408,17 +545,17 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
{'action': 'fight', 'fight': 'crossbow'}\n",
+       "
{'action': {'chosen_action': 'fight', 'weapon': 'crossbow'}}\n",
        "
\n" ], "text/plain": [ - "\u001b[1m{\u001b[0m\u001b[32m'action'\u001b[0m: \u001b[32m'fight'\u001b[0m, \u001b[32m'fight'\u001b[0m: \u001b[32m'crossbow'\u001b[0m\u001b[1m}\u001b[0m\n" + "\u001b[1m{\u001b[0m\u001b[32m'action'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'chosen_action'\u001b[0m: \u001b[32m'fight'\u001b[0m, \u001b[32m'weapon'\u001b[0m: \u001b[32m'crossbow'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -438,7 +575,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -458,14 +595,16 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"action\" choices=\"fight,flight\"/> │ │\n", - " │ │ <string format=\"valid-choices: choices=['crossbow', 'machine gun']\" name=\"fight\" │ │\n", - " │ │ if=\"action==fight\"/> │ │\n", - " │ │ <object name=\"flight\" if=\"action==flight\"> │ │\n", - " │ │ <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', │ │\n", + " │ │ <choice name=\"action\" discriminator=\"chosen_action\"> │ │\n", + " │ │ <case name=\"fight\"> │ │\n", + " │ │ <string name=\"weapon\" format=\"valid-choices: choices=['crossbow', 'machine gun']\"/> │ │\n", + " │ │ </case> │ │\n", + " │ │ <case name=\"flight\"> │ │\n", + " │ │ <string name=\"flight_direction\" format=\"valid-choices: choices=['north', 'south', 'east', │ │\n", " │ │ 'west']\"/> │ │\n", - " │ │ <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/> │ │\n", - " │ │ </object> │ │\n", + " │ │ <integer name=\"distance\" format=\"valid-choices: choices=[1, 2, 3, 4]\"/> │ │\n", + " │ │ </case> │ │\n", + " │ │ </choice> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -480,18 +619,28 @@ " │ │ etc.]}` │ │\n", " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", - " │ │ - `<string name=\"foo\" choices=\"bar,baz\"/><case name=\"bar\" on=\"foo\"><string format=\"valid-choices: ['a', │ │\n", - " │ │ 'b']\"/></case><case name=\"baz\" on=\"foo\"><object><string name=\"qux\" format=\"valid-choices: ['corge', │ │\n", - " │ │ 'grault']\"/><integer name=\"quux\" format=\"valid-choices: [1, 2, 3, 4]\"/></object></case>` => `{'foo': │ │\n", - " │ │ 'baz', 'baz': {'qux': 'corge', 'quux': 1}}` │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n", + " │ │ You are a helpful assistant, able to express yourself purely through JSON, strictly and precisely │ │\n", + " │ │ adhering to the provided XML schemas. │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ │ │\n", - " │ │ {\"action\": \"fight\", \"fight\": \"crossbow\"} │ │\n", + " │ │ { │ │\n", + " │ │ \"action\": { │ │\n", + " │ │ \"chosen_action\": \"fight\", │ │\n", + " │ │ \"weapon\": \"crossbow\" │ │\n", + " │ │ } │ │\n", + " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ {'action': 'fight', 'fight': 'crossbow'} │ │\n", + " │ │ {'action': {'chosen_action': 'fight', 'weapon': 'crossbow'}} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "
\n" @@ -511,14 +660,16 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -533,18 +684,28 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255metc.]}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'foo': \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m'baz', 'baz': {'qux': 'corge', 'quux': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant, able to express yourself purely through JSON, strictly and precisely \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242madhering to the provided XML schemas.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"action\": \"fight\", \"fight\": \"crossbow\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"action\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"chosen_action\": \"fight\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"weapon\": \"crossbow\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{'action': 'fight', 'fight': 'crossbow'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{'action': {'chosen_action': 'fight', 'weapon': 'crossbow'}}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] @@ -560,7 +721,7 @@ ], "metadata": { "kernelspec": { - "display_name": "tiff-env", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -574,9 +735,8 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" - }, - "orig_nbformat": 4 + "version": "3.11.3" + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/docs/examples/syntax_error_free_sql.ipynb b/docs/examples/syntax_error_free_sql.ipynb index 5d0bfe41e..376f40e1c 100644 --- a/docs/examples/syntax_error_free_sql.ipynb +++ b/docs/examples/syntax_error_free_sql.ipynb @@ -23,9 +23,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: sqlvalidator in /home/zayd/workspace/guardrails-poc/.venv/lib/python3.11/site-packages (0.0.20)\n" + ] + } + ], "source": [ "!pip install sqlvalidator" ] @@ -37,16 +45,23 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model.\n", "\n", "In this RAIL spec, we:\n", "\n", "1. Create an `output` schema that returns a single key-value pair. The key should be 'generated_sql' and the value should be the SQL query generated from the natural language, which is syntactically correct." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "XML option:" + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -67,9 +82,9 @@ "\n", "Generate a valid SQL query for the following natural language instruction:\n", "\n", - "{{nl_instruction}}\n", + "${nl_instruction}\n", "\n", - "@complete_json_suffix\n", + "${gr.complete_json_suffix}\n", "\n", "\n", "\n", @@ -77,6 +92,35 @@ "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pydantic model option:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "from guardrails.validators import BugFreeSQL\n", + "from pydantic import BaseModel, Field\n", + "\n", + "prompt = \"\"\"\n", + "\n", + "Generate a valid SQL query for the following natural language instruction:\n", + "\n", + "${nl_instruction}\n", + "\n", + "${gr.complete_json_suffix}\n", + "\"\"\"\n", + "\n", + "class ValidSql(BaseModel):\n", + " generated_sql: str = Field(description=\"Generate SQL for the given natural language instruction.\", validators=[BugFreeSQL(on_fail=\"reask\")])" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -102,26 +146,47 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 30, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/shreyarajpal/guardrails/guardrails/schema.py:187: UserWarning: Validator bug-free-sql is not valid for element string.\n", - " warnings.warn(\n" - ] - } - ], + "outputs": [], "source": [ "import guardrails as gd\n", "\n", - "from rich import print\n", - "\n", + "from rich import print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create the guard from the XML RAIL spec string:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from the Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=ValidSql, prompt=prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -132,7 +197,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -142,28 +207,28 @@ "\n", "Generate a valid SQL query for the following natural language instruction:\n", "\n", - "{nl_instruction}\n", + "${nl_instruction}\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "<output>\n", - " <string name=\"generated_sql\" description=\"Generate SQL for the given natural language instruction.\"/>\n", + " <string name=\"generated_sql\" format=\"bug-free-sql\" description=\"Generate SQL for the given natural language \n", + "instruction.\"/>\n", "</output>\n", "\n", "\n", "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n", "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n", "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n", - "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n", + "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n", "\n", "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n", - "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n", - "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n", + "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n", + "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n", "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n", - "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n", + "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", "\n", - "JSON Object:\n", "\n", "\n" ], @@ -172,28 +237,28 @@ "\n", "Generate a valid SQL query for the following natural language instruction:\n", "\n", - "\u001b[1m{\u001b[0mnl_instruction\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mnl_instruction\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -222,15 +287,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] } ], @@ -259,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -282,14 +346,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -302,8 +366,8 @@
        "    │ │ it into.                                                                                                │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ <output>                                                                                                │ │\n",
-       "    │ │     <string name=\"generated_sql\" description=\"Generate SQL for the given natural language               │ │\n",
-       "    │ │ instruction.\"/>                                                                                         │ │\n",
+       "    │ │     <string name=\"generated_sql\" format=\"bug-free-sql\" description=\"Generate SQL for the given natural  │ │\n",
+       "    │ │ language instruction.\"/>                                                                                │ │\n",
        "    │ │ </output>                                                                                               │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
@@ -320,9 +384,18 @@
        "    │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\"          │ │\n",
        "    │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`                        │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ JSON Object:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ Json Output:                                                                                            │ │\n",
+       "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "    │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "    │ │ ┏━━━━━━┳━━━━━━━━━┓                                                                                      │ │\n",
+       "    │ │ ┃ Role  Content ┃                                                                                      │ │\n",
+       "    │ │ ┡━━━━━━╇━━━━━━━━━┩                                                                                      │ │\n",
+       "    │ │ └──────┴─────────┘                                                                                      │ │\n",
+       "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "    │ │ {\"generated_sql\": \"SELECT name FROM employee ORDER BY salary DESC LIMIT 1\"}                             │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
@@ -334,7 +407,7 @@
       ],
       "text/plain": [
        "Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
+       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
        "    │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -347,8 +420,8 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                        \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -365,9 +438,18 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m                                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m                                                                                     \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n",
+       "    │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"generated_sql\": \"SELECT name FROM employee ORDER BY salary DESC LIMIT 1\"}\u001b[0m\u001b[48;2;245;245;220m                            \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
        "    │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n",
@@ -402,7 +484,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.9.16"
+   "version": "3.11.3"
   },
   "orig_nbformat": 4,
   "vscode": {
diff --git a/docs/examples/text_summarization_quality.ipynb b/docs/examples/text_summarization_quality.ipynb
index 743a7989c..e8c1eb69d 100644
--- a/docs/examples/text_summarization_quality.ipynb
+++ b/docs/examples/text_summarization_quality.ipynb
@@ -30,7 +30,7 @@
      "name": "stdout",
      "output_type": "stream",
      "text": [
-      "Requirement already satisfied: numpy in /Users/krandiash/opt/anaconda3/envs/guardrails/lib/python3.9/site-packages (1.24.2)\n"
+      "Requirement already satisfied: numpy in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (1.25.2)\n"
      ]
     }
    ],
@@ -54,22 +54,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
     "rail_str = \"\"\"\n",
     "\n",
     "\n",
-    "\n",
-    "\n",
     "\n",
     "    \n",
     "\n",
@@ -77,9 +73,9 @@
     "\n",
     "Summarize the following document:\n",
     "\n",
-    "{{document}}\n",
+    "${document}\n",
     "\n",
-    "@complete_json_suffix\n",
+    "${gr.complete_json_suffix}\n",
     "\n",
     "\n",
     "\"\"\""
@@ -139,7 +135,7 @@
        "
\n",
        "Summarize the following document:\n",
        "\n",
-       "{document}\n",
+       "${document}\n",
        "\n",
        "\n",
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
@@ -152,15 +148,14 @@
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n",
        "
\n" ], @@ -168,7 +163,7 @@ "\n", "Summarize the following document:\n", "\n", - "\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mdocument\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", @@ -181,15 +176,14 @@ "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -231,28 +225,27 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { "data": { "text/html": [ - "
Validated Output: {'summary': 'All legislative powers are vested in a Congress of the United States, which consists\n",
-       "of a Senate and House of Representatives. The House of Representatives is composed of members chosen every two \n",
-       "years by the people of the several states, and must meet certain qualifications. Representatives and direct taxes \n",
-       "are apportioned among the states according to their respective numbers. The House of Representatives chooses their \n",
-       "speaker and other officers, and has the sole power of impeachment. When vacancies happen in the representation from\n",
-       "any state, the executive authority thereof shall issue writs of election to fill such vacancies.'}\n",
+       "
Validated Output: {'summary': 'The US Congress consists of a Senate and House of Representatives, with the House of\n",
+       "Representatives being chosen every two years by the people of the several states. Representatives must be at least \n",
+       "25 years old and have been a citizen of the US for seven years. Representation and taxes are apportioned among the \n",
+       "states according to their population, and the number of representatives cannot exceed one for every 30,000 people. \n",
+       "Vacancies are filled by the executive authority of the state. The House of Representatives chooses its speaker and \n",
+       "other officers, and has the sole power of impeachment.'}\n",
        "
\n" ], "text/plain": [ - "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'summary'\u001b[0m: \u001b[32m'All legislative powers are vested in a Congress of the United States, which consists\u001b[0m\n", - "\u001b[32mof a Senate and House of Representatives. The House of Representatives is composed of members chosen every two \u001b[0m\n", - "\u001b[32myears by the people of the several states, and must meet certain qualifications. Representatives and direct taxes \u001b[0m\n", - "\u001b[32mare apportioned among the states according to their respective numbers. The House of Representatives chooses their \u001b[0m\n", - "\u001b[32mspeaker and other officers, and has the sole power of impeachment. When vacancies happen in the representation from\u001b[0m\n", - "\u001b[32many state, the executive authority thereof shall issue writs of election to fill such vacancies.'\u001b[0m\u001b[1m}\u001b[0m\n" + "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'summary'\u001b[0m: \u001b[32m'The US Congress consists of a Senate and House of Representatives, with the House of\u001b[0m\n", + "\u001b[32mRepresentatives being chosen every two years by the people of the several states. Representatives must be at least \u001b[0m\n", + "\u001b[32m25 years old and have been a citizen of the US for seven years. Representation and taxes are apportioned among the \u001b[0m\n", + "\u001b[32mstates according to their population, and the number of representatives cannot exceed one for every 30,000 people. \u001b[0m\n", + "\u001b[32mVacancies are filled by the executive authority of the state. The House of Representatives chooses its speaker and \u001b[0m\n", + "\u001b[32mother officers, and has the sole power of impeachment.'\u001b[0m\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -283,7 +276,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -348,27 +341,36 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ {\"summary\": \"All legislative powers are vested in a Congress of the United States, which consists of a │ │\n", - " │ │ Senate and House of Representatives. The House of Representatives is composed of members chosen every │ │\n", - " │ │ two years by the people of the several states, and must meet certain qualifications. Representatives │ │\n", - " │ │ and direct taxes are apportioned among the states according to their respective numbers. The House of │ │\n", - " │ │ Representatives chooses their speaker and other officers, and has the sole power of impeachment. When │ │\n", - " │ │ vacancies happen in the representation from any state, the executive authority thereof shall issue │ │\n", - " │ │ writs of election to fill such vacancies.\"} │ │\n", + " │ │ {\"summary\": \"The US Congress consists of a Senate and House of Representatives, with the House of │ │\n", + " │ │ Representatives being chosen every two years by the people of the several states. Representatives must │ │\n", + " │ │ be at least 25 years old and have been a citizen of the US for seven years. Representation and taxes │ │\n", + " │ │ are apportioned among the states according to their population, and the number of representatives │ │\n", + " │ │ cannot exceed one for every 30,000 people. Vacancies are filled by the executive authority of the │ │\n", + " │ │ state. The House of Representatives chooses its speaker and other officers, and has the sole power of │ │\n", + " │ │ impeachment.\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", - " │ │ 'summary': 'All legislative powers are vested in a Congress of the United States, which consists of │ │\n", - " │ │ a Senate and House of Representatives. The House of Representatives is composed of members chosen every │ │\n", - " │ │ two years by the people of the several states, and must meet certain qualifications. Representatives │ │\n", - " │ │ and direct taxes are apportioned among the states according to their respective numbers. The House of │ │\n", - " │ │ Representatives chooses their speaker and other officers, and has the sole power of impeachment. When │ │\n", - " │ │ vacancies happen in the representation from any state, the executive authority thereof shall issue │ │\n", - " │ │ writs of election to fill such vacancies.' │ │\n", + " │ │ 'summary': 'The US Congress consists of a Senate and House of Representatives, with the House of │ │\n", + " │ │ Representatives being chosen every two years by the people of the several states. Representatives must │ │\n", + " │ │ be at least 25 years old and have been a citizen of the US for seven years. Representation and taxes │ │\n", + " │ │ are apportioned among the states according to their population, and the number of representatives │ │\n", + " │ │ cannot exceed one for every 30,000 people. Vacancies are filled by the executive authority of the │ │\n", + " │ │ state. The House of Representatives chooses its speaker and other officers, and has the sole power of │ │\n", + " │ │ impeachment.' │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", @@ -434,27 +436,36 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"summary\": \"All legislative powers are vested in a Congress of the United States, which consists of a \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mSenate and House of Representatives. The House of Representatives is composed of members chosen every \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtwo years by the people of the several states, and must meet certain qualifications. Representatives \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mand direct taxes are apportioned among the states according to their respective numbers. The House of \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mRepresentatives chooses their speaker and other officers, and has the sole power of impeachment. When \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mvacancies happen in the representation from any state, the executive authority thereof shall issue \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mwrits of election to fill such vacancies.\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"summary\": \"The US Congress consists of a Senate and House of Representatives, with the House of \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mRepresentatives being chosen every two years by the people of the several states. Representatives must \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mbe at least 25 years old and have been a citizen of the US for seven years. Representation and taxes \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mare apportioned among the states according to their population, and the number of representatives \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mcannot exceed one for every 30,000 people. Vacancies are filled by the executive authority of the \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mstate. The House of Representatives chooses its speaker and other officers, and has the sole power of \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mimpeachment.\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'summary': 'All legislative powers are vested in a Congress of the United States, which consists of\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240ma Senate and House of Representatives. The House of Representatives is composed of members chosen every\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtwo years by the people of the several states, and must meet certain qualifications. Representatives \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mand direct taxes are apportioned among the states according to their respective numbers. The House of \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mRepresentatives chooses their speaker and other officers, and has the sole power of impeachment. When \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mvacancies happen in the representation from any state, the executive authority thereof shall issue \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mwrits of election to fill such vacancies.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'summary': 'The US Congress consists of a Senate and House of Representatives, with the House of \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mRepresentatives being chosen every two years by the people of the several states. Representatives must \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mbe at least 25 years old and have been a citizen of the US for seven years. Representation and taxes \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mare apportioned among the states according to their population, and the number of representatives \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mcannot exceed one for every 30,000 people. Vacancies are filled by the executive authority of the \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mstate. The House of Representatives chooses its speaker and other officers, and has the sole power of \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mimpeachment.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" @@ -480,7 +491,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -519,7 +530,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -584,15 +595,24 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", + " │ │ <string name=\"summary\" description=\"Summarize the given document faithfully.\"/> │ │\n", + " │ │ </string> │ │\n", " │ │ │ │\n", - " │ │ The string \"None`\" is removed from the list \"baz\" in the example above. │ │\n", - " │ │ │ │\n", - " │ │ The object \"baz\" is created with the value \"Some String\" which is set to \"{'foo': 'Some String', │ │\n", - " │ │ 'index': 1}\". │ │\n", + " │ │ The House of Representatives shall chuse their Speaker and other Officers; and shall have the sole │ │\n", + " │ │ Power of Impeachment. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ None │ │\n", @@ -660,15 +680,24 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mThe string \"None`\" is removed from the list \"baz\" in the example above.\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mThe object \"baz\" is created with the value \"Some String\" which is set to \"{'foo': 'Some String', \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m'index': 1}\".\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mThe House of Representatives shall chuse their Speaker and other Officers; and shall have the sole \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPower of Impeachment.\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mNone\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", @@ -701,7 +730,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" }, "orig_nbformat": 4 }, diff --git a/docs/examples/translation_to_specific_language.ipynb b/docs/examples/translation_to_specific_language.ipynb index 5b162cfe2..2ff5775aa 100644 --- a/docs/examples/translation_to_specific_language.ipynb +++ b/docs/examples/translation_to_specific_language.ipynb @@ -23,7 +23,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "tags": [] }, @@ -38,7 +38,7 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model.\n", "\n", "In this RAIL spec, we:\n", "\n", @@ -46,14 +46,10 @@ ] }, { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "tags": [] - }, - "outputs": [], + "cell_type": "markdown", + "metadata": {}, "source": [ - "from profanity_check import predict" + "First we create out custom Validator:" ] }, { @@ -64,34 +60,42 @@ }, "outputs": [], "source": [ - "rail_str = \"\"\"\n", - "\n", - "\n", - "\n", + " return PassResult()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next we define our RAIL spec either as XML:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "rail_str = \"\"\"\n", + "\n", "\n", "\n", " \n", - "\n", "Translate the given statement into english language:\n", "\n", - "{{statement_to_be_translated}}\n", + "${statement_to_be_translated}\n", "\n", - "@complete_json_suffix\n", + "${gr.complete_json_suffix}\n", "\n", "\n", - "\n", "\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or as a Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "prompt = \"\"\"\n", + "Translate the given statement into english language:\n", "\n", + "${statement_to_be_translated}\n", "\n", - "\"\"\"" + "${gr.complete_json_suffix}\n", + "\"\"\"\n", + "\n", + "class Translation(BaseModel):\n", + " translated_statement: str = Field(\n", + " description=\"Translate the given statement into english language\",\n", + " validators=[IsProfanityFree(on_fail=\"fix\")]\n", + " )" ] }, { @@ -143,19 +173,49 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "import guardrails as gd\n", + "\n", + "from rich import print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From XML:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": { "tags": [] }, "outputs": [], "source": [ - "import guardrails as gd\n", - "\n", - "from rich import print\n", - "\n", "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from our Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=Translation, prompt=prompt)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -165,7 +225,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 10, "metadata": { "tags": [] }, @@ -174,63 +234,59 @@ "data": { "text/html": [ "
\n",
-       "\n",
        "Translate the given statement into english language:\n",
        "\n",
-       "{statement_to_be_translated}\n",
+       "${statement_to_be_translated}\n",
        "\n",
        "\n",
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "<output>\n",
-       "    <string name=\"translated_statement\" description=\"Translate the given statement into english language\" \n",
-       "format=\"is-profanity-free: \"/>\n",
+       "    <string name=\"translated_statement\" format=\"is-profanity-free\" description=\"Translate the given statement into \n",
+       "english language\"/>\n",
        "</output>\n",
        "\n",
        "\n",
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n",
        "
\n" ], "text/plain": [ - "\n", "\n", "Translate the given statement into english language:\n", "\n", - "\u001b[1m{\u001b[0mstatement_to_be_translated\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mstatement_to_be_translated\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -266,15 +322,14 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { @@ -314,7 +369,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -324,7 +379,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n", " │ │ │ │\n", - " │ │ │ │\n", " │ │ Translate the given statement into english language: │ │\n", " │ │ │ │\n", " │ │ quesadilla de pollo │ │\n", @@ -334,8 +388,8 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"translated_statement\" description=\"Translate the given statement into english │ │\n", - " │ │ language\" format=\"is-profanity-free: \"/> │ │\n", + " │ │ <string name=\"translated_statement\" format=\"is-profanity-free\" description=\"Translate the given │ │\n", + " │ │ statement into english language\"/> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -352,9 +406,18 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", " │ │ {\"translated_statement\": \"Chicken Quesadilla\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -369,7 +432,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mTranslate the given statement into english language:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mquesadilla de pollo\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -379,8 +441,8 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -397,9 +459,18 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"translated_statement\": \"Chicken Quesadilla\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", @@ -438,8 +509,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { @@ -488,7 +558,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n", " │ │ │ │\n", - " │ │ │ │\n", " │ │ Translate the given statement into english language: │ │\n", " │ │ │ │\n", " │ │ убей себя │ │\n", @@ -498,8 +567,8 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"translated_statement\" description=\"Translate the given statement into english │ │\n", - " │ │ language\" format=\"is-profanity-free: \"/> │ │\n", + " │ │ <string name=\"translated_statement\" format=\"is-profanity-free\" description=\"Translate the given │ │\n", + " │ │ statement into english language\"/> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -516,9 +585,18 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", " │ │ {\"translated_statement\": \"Kill yourself\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -533,7 +611,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mTranslate the given statement into english language:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mубей себя\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -543,8 +620,8 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -561,9 +638,18 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"translated_statement\": \"Kill yourself\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", @@ -598,7 +684,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/docs/examples/translation_with_quality_check.ipynb b/docs/examples/translation_with_quality_check.ipynb index 73cc39b74..d6f78de21 100644 --- a/docs/examples/translation_with_quality_check.ipynb +++ b/docs/examples/translation_with_quality_check.ipynb @@ -31,11 +31,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting inspiredco\n", + " Downloading inspiredco-0.0.2-py3-none-any.whl (6.2 kB)\n", + "Requirement already satisfied: requests>=2.27.0 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from inspiredco) (2.31.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (3.2.0)\n", + "Requirement already satisfied: idna<4,>=2.5 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (3.4)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (2.0.4)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /Users/calebcourier/Projects/shreyar/guardrails/.venv/lib/python3.11/site-packages (from requests>=2.27.0->inspiredco) (2023.7.22)\n", + "Installing collected packages: inspiredco\n", + "Successfully installed inspiredco-0.0.2\n" + ] + } + ], "source": [ "!pip install inspiredco\n" ] @@ -46,7 +62,7 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model.\n", "\n", "In this RAIL spec, we:\n", "\n", @@ -55,7 +71,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": { "tags": [] }, @@ -64,9 +80,16 @@ "from inspiredco import critique" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our RAIL spec as an XML string:" + ] + }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 13, "metadata": { "tags": [] }, @@ -75,38 +98,6 @@ "rail_str = \"\"\"\n", "\n", "\n", - "\n", - "\n", "\n", " \n", - "\n", "Translate the given statement into the English language:\n", "\n", - "{{statement_to_be_translated}}\n", + "${statement_to_be_translated}\n", "\n", - "@complete_json_suffix\n", + "${gr.complete_json_suffix}\n", "\n", "\n", "\n", @@ -133,6 +123,37 @@ "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or as a Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "from guardrails.validators import IsHighQualityTranslation\n", + "\n", + "prompt = \"\"\"\n", + "Translate the given statement into the English language:\n", + "\n", + "${statement_to_be_translated}\n", + "\n", + "${gr.complete_json_suffix}\n", + "\"\"\"\n", + "\n", + "class Translation(BaseModel):\n", + " translated_statement: str = Field(\n", + " description=\"Translate the given statement into the English language\",\n", + " validators=[IsHighQualityTranslation(on_fail=\"fix\")]\n", + " )" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -158,19 +179,49 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "import guardrails as gd\n", + "\n", + "from rich import print" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From our RAIL string:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, "metadata": { "tags": [] }, "outputs": [], "source": [ - "import guardrails as gd\n", - "\n", - "from rich import print\n", - "\n", "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or from our Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=Translation, prompt=prompt)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -180,7 +231,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 18, "metadata": { "tags": [] }, @@ -189,63 +240,59 @@ "data": { "text/html": [ "
\n",
-       "\n",
        "Translate the given statement into the English language:\n",
        "\n",
-       "{statement_to_be_translated}\n",
+       "${statement_to_be_translated}\n",
        "\n",
        "\n",
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "<output>\n",
-       "    <string name=\"translated_statement\" description=\"Translate the given statement into the English language\" \n",
-       "format=\"is-high-quality-translation: \"/>\n",
+       "    <string name=\"translated_statement\" format=\"is-high-quality-translation\" description=\"Translate the given \n",
+       "statement into the English language\"/>\n",
        "</output>\n",
        "\n",
        "\n",
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n",
        "
\n" ], "text/plain": [ - "\n", "\n", "Translate the given statement into the English language:\n", "\n", - "\u001b[1m{\u001b[0mstatement_to_be_translated\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mstatement_to_be_translated\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -281,15 +328,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { @@ -309,9 +355,11 @@ "source": [ "import openai\n", "\n", + "statement = \"これは簡単に翻訳できるかもしれない。\"\n", "raw_llm_response, validated_response = guard(\n", " openai.Completion.create,\n", - " prompt_params={'statement_to_be_translated': 'これは簡単に翻訳できるかもしれない。'},\n", + " prompt_params={'statement_to_be_translated': statement},\n", + " metadata={'translation_source': statement},\n", " engine='text-davinci-003',\n", " max_tokens=2048,\n", " temperature=0\n", @@ -330,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -340,7 +388,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n", " │ │ │ │\n", - " │ │ │ │\n", " │ │ Translate the given statement into the English language: │ │\n", " │ │ │ │\n", " │ │ これは簡単に翻訳できるかもしれない。 │ │\n", @@ -350,8 +397,8 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"translated_statement\" description=\"Translate the given statement into the English │ │\n", - " │ │ language\" format=\"is-high-quality-translation: \"/> │ │\n", + " │ │ <string name=\"translated_statement\" format=\"is-high-quality-translation\" description=\"Translate the │ │\n", + " │ │ given statement into the English language\"/> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -368,9 +415,18 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", " │ │ {\"translated_statement\": \"This may be easy to translate.\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -385,7 +441,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mTranslate the given statement into the English language:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mこれは簡単に翻訳できるかもしれない。\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -395,8 +450,8 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -413,9 +468,18 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"translated_statement\": \"This may be easy to translate.\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", @@ -445,7 +509,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 21, "metadata": { "tags": [] }, @@ -454,18 +518,17 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { "data": { "text/html": [ - "
Validated Output: {'translated_statement': ''}\n",
+       "
Validated Output: {'translated_statement': \"It is so bad that it's a letdown.\"}\n",
        "
\n" ], "text/plain": [ - "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'translated_statement'\u001b[0m: \u001b[32m''\u001b[0m\u001b[1m}\u001b[0m\n" + "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'translated_statement'\u001b[0m: \u001b[32m\"It is so bad that it's a letdown.\"\u001b[0m\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -476,6 +539,7 @@ "raw_llm_response, validated_response = guard(\n", " openai.Completion.create,\n", " prompt_params={'statement_to_be_translated': 'ドン引きするほど翻訳が悪い。'},\n", + " metadata={'translation_source': 'ドン引きするほど翻訳が悪い。'},\n", " engine='text-davinci-003',\n", " max_tokens=2048,\n", " temperature=0\n", @@ -494,7 +558,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -504,7 +568,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n", " │ │ │ │\n", - " │ │ │ │\n", " │ │ Translate the given statement into the English language: │ │\n", " │ │ │ │\n", " │ │ ドン引きするほど翻訳が悪い。 │ │\n", @@ -514,8 +577,8 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"translated_statement\" description=\"Translate the given statement into the English │ │\n", - " │ │ language\" format=\"is-high-quality-translation: \"/> │ │\n", + " │ │ <string name=\"translated_statement\" format=\"is-high-quality-translation\" description=\"Translate the │ │\n", + " │ │ given statement into the English language\"/> │ │\n", " │ │ </output> │ │\n", " │ │ │ │\n", " │ │ │ │\n", @@ -532,14 +595,23 @@ " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", " │ │ │ │\n", - " │ │ JSON Object: │ │\n", + " │ │ │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + " │ │ ┃ Role Content ┃ │ │\n", + " │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + " │ │ └──────┴─────────┘ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ {\"translated_statement\": \"It's so bad that it's a letdown.\"} │ │\n", + " │ │ {\"translated_statement\": \"It is so bad that it's a letdown.\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ {'translated_statement': ''} │ │\n", + " │ │ {'translated_statement': \"It is so bad that it's a letdown.\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "
\n" @@ -549,7 +621,6 @@ "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n", " │ \u001b[48;2;240;248;255m╭─\u001b[0m\u001b[48;2;240;248;255m───────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m Prompt \u001b[0m\u001b[48;2;240;248;255m────────────────────────────────────────────────\u001b[0m\u001b[48;2;240;248;255m─╮\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mTranslate the given statement into the English language:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mドン引きするほど翻訳が悪い。\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -559,8 +630,8 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mit into.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -577,14 +648,23 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON Object:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"translated_statement\": \"It's so bad that it's a letdown.\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"translated_statement\": \"It is so bad that it's a letdown.\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╭─\u001b[0m\u001b[48;2;240;255;240m──────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m Validated Output \u001b[0m\u001b[48;2;240;255;240m───────────────────────────────────────────\u001b[0m\u001b[48;2;240;255;240m─╮\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{'translated_statement': ''}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m{'translated_statement': \"It is so bad that it's a letdown.\"}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] @@ -614,7 +694,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/docs/examples/valid_chess_moves.ipynb b/docs/examples/valid_chess_moves.ipynb index d8ccf9ad6..2945d8202 100644 --- a/docs/examples/valid_chess_moves.ipynb +++ b/docs/examples/valid_chess_moves.ipynb @@ -32,9 +32,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting chess\n", + " Obtaining dependency information for chess from https://files.pythonhosted.org/packages/d6/d8/15cfcb738d2518daf04d34b23419bd359cbd8e09da50778ebac521774fc8/chess-1.10.0-py3-none-any.whl.metadata\n", + " Downloading chess-1.10.0-py3-none-any.whl.metadata (19 kB)\n", + "Downloading chess-1.10.0-py3-none-any.whl (154 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m154.4/154.4 kB\u001b[0m \u001b[31m3.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hInstalling collected packages: chess\n", + "Successfully installed chess-1.10.0\n" + ] + } + ], "source": [ "!pip install chess" ] @@ -46,27 +60,25 @@ "source": [ "## Step 1: Create the RAIL Spec\n", "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", - "\n", - "Here, we request:\n", - "\n" + "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md). We will also show the same RAIL spec in a code-first format using a Pydantic model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we define a custom Validator:" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ - "rail_str = \"\"\"\n", - "\n", - "\n", - "\n", - "\n", + " return PassResult()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we can define our RAIL spec either as XML:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "rail_str = \"\"\"\n", + "\n", "\n", "\n", " \n", @@ -103,14 +126,42 @@ "\n", "\n", "Generate a move for the chess board. The board is currently in the following state:\n", - "{{board_state}}\n", - "@complete_json_suffix\n", + "${board_state}\n", + "${gr.complete_json_suffix}\n", "\n", "\n", "\n", "\"\"\"" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or as a Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "from pydantic import BaseModel, Field\n", + "\n", + "prompt = \"\"\"\n", + "Generate a move for the chess board. The board is currently in the following state:\n", + "${board_state}\n", + "${gr.complete_json_suffix}\n", + "\"\"\"\n", + "\n", + "class ChessMove(BaseModel):\n", + " move: str = Field(\n", + " description=\"A move in standard algebraic notation.\",\n", + " validators=[IsValidChessMove(on_fail=\"reask\")]\n", + " )" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -124,15 +175,38 @@ "3. Compiles the schema and type info from the RAIL spec and adds it to the prompt." ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From XML:" + ] + }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From a Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=ChessMove, prompt=prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -143,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -151,60 +225,52 @@ "text/html": [ "
\n",
        "Generate a move for the chess board. The board is currently in the following state:\n",
-       "{board_state}\n",
+       "${board_state}\n",
        "\n",
        "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
        "\n",
        "<output>\n",
-       "    <string description=\"A move in standard algebraic notation.\" name=\"move\" required=\"true\" \n",
-       "format=\"is-valid-chess-move\"/>\n",
+       "    <string name=\"move\" format=\"is-valid-chess-move\" description=\"A move in standard algebraic notation.\"/>\n",
        "</output>\n",
        "\n",
        "\n",
-       "\n",
-       "\n",
        "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
        "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
        "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise. If you are unsure anywhere, enter `None`.\n",
+       "specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\n",
        "\n",
        "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
+       "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n",
+       "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n",
        "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
        "\n",
-       "JSON Object:\n",
        "\n",
        "
\n" ], "text/plain": [ "\n", "Generate a move for the chess board. The board is currently in the following state:\n", - "\u001b[1m{\u001b[0mboard_state\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mboard_state\u001b[1m}\u001b[0m\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", "\n", "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", "\n", "\n", - "\n", - "\n", "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m`.\u001b[0m\n", + "\u001b[39mspecific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", - "JSON Object:\n", "\n" ] }, @@ -226,17 +292,26 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "
r n b q k b n r\np p p p p p p p\n. . . . . . . .\n. . . . . . . .\n. . . . . . . .\n. . . . . . . .\nP P P P P P P P\nR N B Q K B N R
", + "image/svg+xml": [ + "
r n b q k b n r\n",
+       "p p p p p p p p\n",
+       ". . . . . . . .\n",
+       ". . . . . . . .\n",
+       ". . . . . . . .\n",
+       ". . . . . . . .\n",
+       "P P P P P P P P\n",
+       "R N B Q K B N R
" + ], "text/plain": [ "Board('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1')" ] }, - "execution_count": 6, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -255,9 +330,17 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "import openai\n", "\n", @@ -285,7 +368,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -308,17 +391,26 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "
r n b q k b n r\np p p p p p p p\n. . . . . . . .\n. . . . . . . .\n. . . . P . . .\n. . . . . . . .\nP P P P . P P P\nR N B Q K B N R
", + "image/svg+xml": [ + "
r n b q k b n r\n",
+       "p p p p p p p p\n",
+       ". . . . . . . .\n",
+       ". . . . . . . .\n",
+       ". . . . P . . .\n",
+       ". . . . . . . .\n",
+       "P P P P . P P P\n",
+       "R N B Q K B N R
" + ], "text/plain": [ "Board('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1')" ] }, - "execution_count": 10, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -337,17 +429,26 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 19, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "
r n b q k b n r\np p p p . p p p\n. . . . . . . .\n. . . . p . . .\n. . . . P . . .\n. . . . . . . .\nP P P P . P P P\nR N B Q K B N R
", + "image/svg+xml": [ + "
r n b q k b n r\n",
+       "p p p p . p p p\n",
+       ". . . . . . . .\n",
+       ". . . . p . . .\n",
+       ". . . . P . . .\n",
+       ". . . . . . . .\n",
+       "P P P P . P P P\n",
+       "R N B Q K B N R
" + ], "text/plain": [ "Board('rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2')" ] }, - "execution_count": 11, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -367,9 +468,17 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + } + ], "source": [ "raw_llm_response, validated_response = guard(\n", " openai.Completion.create,\n", @@ -386,17 +495,26 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 24, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "
r n b q k b n r\np p p p . p p p\n. . . . . . . .\n. . . . p . . .\n. . . . P . . .\n. . . . . N . .\nP P P P . P P P\nR N B Q K B . R
", + "image/svg+xml": [ + "
r n b q k b n r\n",
+       "p p p p . p p p\n",
+       ". . . . . . . .\n",
+       ". . . . p . . .\n",
+       ". . . . P . . .\n",
+       ". . . . . N . .\n",
+       "P P P P . P P P\n",
+       "R N B Q K B . R
" + ], "text/plain": [ "Board('rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2')" ] }, - "execution_count": 14, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -407,17 +525,26 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 25, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": "
r . b q k b n r\np p p p . p p p\n. . n . . . . .\n. . . . p . . .\n. . . . P . . .\n. . . . . N . .\nP P P P . P P P\nR N B Q K B . R
", + "image/svg+xml": [ + "
r . b q k b n r\n",
+       "p p p p . p p p\n",
+       ". . n . . . . .\n",
+       ". . . . p . . .\n",
+       ". . . . P . . .\n",
+       ". . . . . N . .\n",
+       "P P P P . P P P\n",
+       "R N B Q K B . R
" + ], "text/plain": [ "Board('r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3')" ] }, - "execution_count": 15, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -444,7 +571,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/guardrails_ai/configuration.md b/docs/guardrails_ai/configuration.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/guardrails_ai/faq.md b/docs/guardrails_ai/faq.md new file mode 100644 index 000000000..f776f1c88 --- /dev/null +++ b/docs/guardrails_ai/faq.md @@ -0,0 +1,21 @@ +# Frequently Asked Questions + +## I'm encountering an XMLSyntaxError when creating a `Guard` object from a `RAIL` specification. What should I do? + +Make sure that you are escaping the `&` character in your `RAIL` specification. The `&` character has a special meaning in XML, and so you need to escape it with `&`. For example, if you have a prompt like this: + +```xml + + This is a prompt with an & character. + +``` + +You need to escape the `&` character like this: + +```xml + + This is a prompt with an & character. + +``` + +If you're still encountering issues, please [open an issue](https://github.com/ShreyaR/guardrails/issues/new) and we'll help you out! diff --git a/docs/getting_started.ipynb b/docs/guardrails_ai/getting_started.ipynb similarity index 76% rename from docs/getting_started.ipynb rename to docs/guardrails_ai/getting_started.ipynb index bb0a80f7b..52681cb98 100644 --- a/docs/getting_started.ipynb +++ b/docs/guardrails_ai/getting_started.ipynb @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -64,12 +64,10 @@ "\n", "`RAIL` is a flavor of XML (standing for **R**eliable **AI** markup **L**anguage) that describes the expected structure and type of the output of the LLM, the quality criteria for the output to be valid and corrective actions to be taken if the output is invalid.\n", "\n", - "A `RAIL` spec is composed of 3 main components:\n", + "A `RAIL` spec is composed of 2 main components:\n", "\n", "1. Output schema\n", "2. Prompt\n", - "3. (Optional) Script\n", - "\n", "\n", "### Output schema\n", "\n", @@ -206,9 +204,9 @@ "Given the following doctor's notes about a patient,\n", "please extract a dictionary that contains the patient's information. \n", "\n", - "{{doctors_notes}} \n", + "${doctors_notes} \n", "\n", - "@complete_json_suffix_v2 \n", + "${gr.complete_json_suffix_v2} \n", "\n", "```\n", "\n", @@ -234,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -263,15 +261,15 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/shreyarajpal/anaconda3/envs/tiff-env/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar. (This may have returned Python scalars in past versions.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n", + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" ] }, { @@ -285,7 +283,7 @@ " {'symptom': 'macular rash', 'affected area': 'head'},\n", " {'symptom': 'itchy, flaky, slightly scaly', 'affected area': 'neck'}\n", " ],\n", - " 'current_meds': [{'medication': 'OTC steroid cream', 'response': 'Moderate'}]\n", + " 'current_meds': [{'medication': 'OTC steroid cream', 'response': 'Moderate response'}]\n", " }\n", "}\n", "\n" @@ -299,7 +297,7 @@ " \u001b[1m{\u001b[0m\u001b[32m'symptom'\u001b[0m: \u001b[32m'macular rash'\u001b[0m, \u001b[32m'affected area'\u001b[0m: \u001b[32m'head'\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\u001b[32m'symptom'\u001b[0m: \u001b[32m'itchy, flaky, slightly scaly'\u001b[0m, \u001b[32m'affected area'\u001b[0m: \u001b[32m'neck'\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m,\n", - " \u001b[32m'current_meds'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'medication'\u001b[0m: \u001b[32m'OTC steroid cream'\u001b[0m, \u001b[32m'response'\u001b[0m: \u001b[32m'Moderate'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[32m'current_meds'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'medication'\u001b[0m: \u001b[32m'OTC steroid cream'\u001b[0m, \u001b[32m'response'\u001b[0m: \u001b[32m'Moderate response'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -351,7 +349,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -404,7 +402,17 @@ "│ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │\n", "│ │\n", "│ │\n", + "│ │\n", + "│ Json Output: │\n", + "│ │\n", + "│ │\n", "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "╭──────────────────────────────────────────────── Message History ────────────────────────────────────────────────╮\n", + "│ ┏━━━━━━┳━━━━━━━━━┓ │\n", + "│ ┃ Role Content ┃ │\n", + "│ ┡━━━━━━╇━━━━━━━━━┩ │\n", + "│ └──────┴─────────┘ │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "╭──────────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────────╮\n", "│ { │\n", "│ \"patient_info\": { │\n", @@ -423,7 +431,7 @@ "│ \"current_meds\": [ │\n", "│ { │\n", "│ \"medication\": \"OTC steroid cream\", │\n", - "│ \"response\": \"Moderate\" │\n", + "│ \"response\": \"Moderate response\" │\n", "│ } │\n", "│ ] │\n", "│ } │\n", @@ -437,25 +445,41 @@ "│ 'symptoms': [ │\n", "│ { │\n", "│ 'symptom': 'macular rash', │\n", - "│ 'affected area': ReAsk( │\n", + "│ 'affected area': FieldReAsk( │\n", "│ incorrect_value='face & hair', │\n", - "│ error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\", │\n", - "│ fix_value=None, │\n", + "│ fail_results=[ │\n", + "│ FailResult( │\n", + "│ outcome='fail', │\n", + "│ metadata=None, │\n", + "│ error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\", │\n", + "│ fix_value=None │\n", + "│ ) │\n", + "│ ], │\n", "│ path=['patient_info', 'symptoms', 0, 'affected area'] │\n", "│ ) │\n", "│ }, │\n", "│ { │\n", "│ 'symptom': 'itchy, flaky, slightly scaly', │\n", - "│ 'affected area': ReAsk( │\n", + "│ 'affected area': FieldReAsk( │\n", "│ incorrect_value='beard, eyebrows & nares', │\n", - "│ error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\", │\n", - "│ fix_value=None, │\n", + "│ fail_results=[ │\n", + "│ FailResult( │\n", + "│ outcome='fail', │\n", + "│ metadata=None, │\n", + "│ error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', │\n", + "│ 'chest'].\", │\n", + "│ fix_value=None │\n", + "│ ) │\n", + "│ ], │\n", "│ path=['patient_info', 'symptoms', 1, 'affected area'] │\n", "│ ) │\n", "│ } │\n", "│ ], │\n", "│ 'current_meds': [ │\n", - "│ {'medication': 'OTC steroid cream', 'response': 'Moderate'} │\n", + "│ { │\n", + "│ 'medication': 'OTC steroid cream', │\n", + "│ 'response': 'Moderate response' │\n", + "│ } │\n", "│ ] │\n", "│ } │\n", "│ } │\n", @@ -510,7 +534,17 @@ "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mformat=\"1-indexed\" />
` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n", + "\u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m\n", + "\u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m\n", + "\u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m\n", + "\u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m\n", + "\u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m\n", + "\u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n", "\u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"patient_info\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", @@ -529,7 +563,7 @@ "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"current_meds\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"medication\": \"OTC steroid cream\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", - "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"response\": \"Moderate\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", + "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"response\": \"Moderate response\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ]\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", @@ -543,25 +577,41 @@ "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptoms': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptom': 'macular rash',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='face & hair',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['patient_info', 'symptoms', 0, 'affected area']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptom': 'itchy, flaky, slightly scaly',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='beard, eyebrows & nares',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['patient_info', 'symptoms', 1, 'affected area']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'current_meds': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'medication': 'OTC steroid cream', 'response': 'Moderate'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'medication': 'OTC steroid cream',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'response': 'Moderate response'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", @@ -590,7 +640,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -606,13 +656,17 @@ "│ { │\n", "│ \"affected area\": { │\n", "│ \"incorrect_value\": \"face & hair\", │\n", - "│ \"error_message\": \"Value face & hair is not in choices ['head', 'neck', 'chest'].\" │\n", + "│ \"error_messages\": [ │\n", + "│ \"Value face & hair is not in choices ['head', 'neck', 'chest'].\" │\n", + "│ ] │\n", "│ } │\n", "│ }, │\n", "│ { │\n", "│ \"affected area\": { │\n", "│ \"incorrect_value\": \"beard, eyebrows & nares\", │\n", - "│ \"error_message\": \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\" │\n", + "│ \"error_messages\": [ │\n", + "│ \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\" │\n", + "│ ] │\n", "│ } │\n", "│ } │\n", "│ ] │\n", @@ -642,13 +696,30 @@ "│ JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects │\n", "│ and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. │\n", "│ │\n", - "│ Here are examples of simple (XML, JSON) pairs that show the expected behavior: │\n", - "│ - `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}` │\n", - "│ - `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}` │\n", - "│ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │\n", - "│ format=\"1-indexed\" /></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}` │\n", + "│ │\n", + "│ Json Output: │\n", + "│ │\n", "│ │\n", "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "╭───────────────────────────────────────────────── Instructions ──────────────────────────────────────────────────╮\n", + "│ │\n", + "│ You are a helpful assistant only capable of communicating with valid JSON, and no other text. │\n", + "│ │\n", + "│ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` │\n", + "│ attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The │\n", + "│ JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects │\n", + "│ and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. │\n", + "│ │\n", + "│ Here are examples of simple (XML, JSON) pairs that show the expected behavior: │\n", + "│ - `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}` │\n", + "│ - `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}` │\n", + "│ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │\n", + "│ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │\n", + "│ │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", + "╭──────────────────────────────────────────────── Message History ────────────────────────────────────────────────╮\n", + "│ No message history. │\n", + "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "╭──────────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────────╮\n", "│ { │\n", "│ \"patient_info\": { │\n", @@ -676,7 +747,10 @@ "│ } │\n", "│ ], │\n", "│ 'current_meds': [ │\n", - "│ {'medication': 'OTC steroid cream', 'response': 'Moderate'} │\n", + "│ { │\n", + "│ 'medication': 'OTC steroid cream', │\n", + "│ 'response': 'Moderate response' │\n", + "│ } │\n", "│ ] │\n", "│ } │\n", "│ } │\n", @@ -694,13 +768,17 @@ "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"affected area\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"face & hair\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"Value face & hair is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"Value face & hair is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"affected area\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"beard, eyebrows & nares\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", @@ -730,13 +808,30 @@ "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mand specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'foo': 'example one'}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", - "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", + "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m\n", "\u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n", + "\u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant only capable of communicating with valid JSON, and no other text.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mJSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mand specific types. Be correct and concise. If you are unsure anywhere, enter `null`.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'foo': 'example one'}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m\n", + "\u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n", + "\u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m\n", + "\u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235mNo message history.\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m\n", + "\u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n", "\u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", "\u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"patient_info\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m\n", @@ -764,7 +859,10 @@ "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'current_meds': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", - "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'medication': 'OTC steroid cream', 'response': 'Moderate'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'medication': 'OTC steroid cream',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'response': 'Moderate response'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", + "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", "\u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m\n", @@ -801,7 +899,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -858,7 +956,17 @@ "│ │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", "│ │ │ │ │\n", "│ │ │ │ │\n", + "│ │ │ │ │\n", + "│ │ │ Json Output: │ │\n", + "│ │ │ │ │\n", + "│ │ │ │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + "│ │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + "│ │ │ ┏━━━━━━┳━━━━━━━━━┓ │ │\n", + "│ │ │ ┃ Role Content ┃ │ │\n", + "│ │ │ ┡━━━━━━╇━━━━━━━━━┩ │ │\n", + "│ │ │ └──────┴─────────┘ │ │\n", + "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", "│ │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", "│ │ │ { │ │\n", "│ │ │ \"patient_info\": { │ │\n", @@ -877,7 +985,7 @@ "│ │ │ \"current_meds\": [ │ │\n", "│ │ │ { │ │\n", "│ │ │ \"medication\": \"OTC steroid cream\", │ │\n", - "│ │ │ \"response\": \"Moderate\" │ │\n", + "│ │ │ \"response\": \"Moderate response\" │ │\n", "│ │ │ } │ │\n", "│ │ │ ] │ │\n", "│ │ │ } │ │\n", @@ -891,26 +999,42 @@ "│ │ │ 'symptoms': [ │ │\n", "│ │ │ { │ │\n", "│ │ │ 'symptom': 'macular rash', │ │\n", - "│ │ │ 'affected area': ReAsk( │ │\n", + "│ │ │ 'affected area': FieldReAsk( │ │\n", "│ │ │ incorrect_value='face & hair', │ │\n", - "│ │ │ error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\", │ │\n", - "│ │ │ fix_value=None, │ │\n", + "│ │ │ fail_results=[ │ │\n", + "│ │ │ FailResult( │ │\n", + "│ │ │ outcome='fail', │ │\n", + "│ │ │ metadata=None, │ │\n", + "│ │ │ error_message=\"Value face & hair is not in choices ['head', 'neck', │ │\n", + "│ │ │ 'chest'].\", │ │\n", + "│ │ │ fix_value=None │ │\n", + "│ │ │ ) │ │\n", + "│ │ │ ], │ │\n", "│ │ │ path=['patient_info', 'symptoms', 0, 'affected area'] │ │\n", "│ │ │ ) │ │\n", "│ │ │ }, │ │\n", "│ │ │ { │ │\n", "│ │ │ 'symptom': 'itchy, flaky, slightly scaly', │ │\n", - "│ │ │ 'affected area': ReAsk( │ │\n", + "│ │ │ 'affected area': FieldReAsk( │ │\n", "│ │ │ incorrect_value='beard, eyebrows & nares', │ │\n", - "│ │ │ error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', │ │\n", - "│ │ │ 'chest'].\", │ │\n", - "│ │ │ fix_value=None, │ │\n", + "│ │ │ fail_results=[ │ │\n", + "│ │ │ FailResult( │ │\n", + "│ │ │ outcome='fail', │ │\n", + "│ │ │ metadata=None, │ │\n", + "│ │ │ error_message=\"Value beard, eyebrows & nares is not in choices ['head', │ │\n", + "│ │ │ 'neck', 'chest'].\", │ │\n", + "│ │ │ fix_value=None │ │\n", + "│ │ │ ) │ │\n", + "│ │ │ ], │ │\n", "│ │ │ path=['patient_info', 'symptoms', 1, 'affected area'] │ │\n", "│ │ │ ) │ │\n", "│ │ │ } │ │\n", "│ │ │ ], │ │\n", "│ │ │ 'current_meds': [ │ │\n", - "│ │ │ {'medication': 'OTC steroid cream', 'response': 'Moderate'} │ │\n", + "│ │ │ { │ │\n", + "│ │ │ 'medication': 'OTC steroid cream', │ │\n", + "│ │ │ 'response': 'Moderate response' │ │\n", + "│ │ │ } │ │\n", "│ │ │ ] │ │\n", "│ │ │ } │ │\n", "│ │ │ } │ │\n", @@ -927,13 +1051,17 @@ " │ │ { │ │\n", " │ │ \"affected area\": { │ │\n", " │ │ \"incorrect_value\": \"face & hair\", │ │\n", - " │ │ \"error_message\": \"Value face & hair is not in choices ['head', 'neck', 'chest'].\" │ │\n", + " │ │ \"error_messages\": [ │ │\n", + " │ │ \"Value face & hair is not in choices ['head', 'neck', 'chest'].\" │ │\n", + " │ │ ] │ │\n", " │ │ } │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"affected area\": { │ │\n", " │ │ \"incorrect_value\": \"beard, eyebrows & nares\", │ │\n", - " │ │ \"error_message\": \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\" │ │\n", + " │ │ \"error_messages\": [ │ │\n", + " │ │ \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\" │ │\n", + " │ │ ] │ │\n", " │ │ } │ │\n", " │ │ } │ │\n", " │ │ ] │ │\n", @@ -964,14 +1092,32 @@ " │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, │ │\n", " │ │ enter `null`. │ │\n", " │ │ │ │\n", - " │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior: │ │\n", - " │ │ - `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}` │ │\n", - " │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', │ │\n", - " │ │ etc.]}}` │ │\n", - " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", - " │ │ format=\"1-indexed\" /></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}` │ │\n", + " │ │ │ │\n", + " │ │ Json Output: │ │\n", + " │ │ │ │\n", " │ │ │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭───────────────────────────────────────────── Instructions ──────────────────────────────────────────────╮ │\n", + " │ │ │ │\n", + " │ │ You are a helpful assistant only capable of communicating with valid JSON, and no other text. │ │\n", + " │ │ │ │\n", + " │ │ ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the │ │\n", + " │ │ `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding │ │\n", + " │ │ XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. │ │\n", + " │ │ requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, │ │\n", + " │ │ enter `null`. │ │\n", + " │ │ │ │\n", + " │ │ Here are examples of simple (XML, JSON) pairs that show the expected behavior: │ │\n", + " │ │ - `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}` │ │\n", + " │ │ - `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', │ │\n", + " │ │ etc.]}` │ │\n", + " │ │ - `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" │ │\n", + " │ │ format=\"1-indexed\" /></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}` │ │\n", + " │ │ │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", + " │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n", + " │ │ No message history. │ │\n", + " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", " │ │ \"patient_info\": { │ │\n", @@ -999,7 +1145,10 @@ " │ │ } │ │\n", " │ │ ], │ │\n", " │ │ 'current_meds': [ │ │\n", - " │ │ {'medication': 'OTC steroid cream', 'response': 'Moderate'} │ │\n", + " │ │ { │ │\n", + " │ │ 'medication': 'OTC steroid cream', │ │\n", + " │ │ 'response': 'Moderate response' │ │\n", + " │ │ } │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", " │ │ } │ │\n", @@ -1059,7 +1208,17 @@ "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mformat=\"1-indexed\" />
` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┏━━━━━━┳━━━━━━━━━┓\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mRole\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[1;48;2;231;223;235mContent\u001b[0m\u001b[1;48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┃\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m┡━━━━━━╇━━━━━━━━━┩\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m└──────┴─────────┘\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + "│ │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"patient_info\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", @@ -1078,7 +1237,7 @@ "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"current_meds\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"medication\": \"OTC steroid cream\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"response\": \"Moderate\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"response\": \"Moderate response\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m ]\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", @@ -1092,26 +1251,42 @@ "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptoms': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptom': 'macular rash',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='face & hair',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value face & hair is not in choices ['head', 'neck', 'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value face & hair is not in choices ['head', 'neck', \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['patient_info', 'symptoms', 0, 'affected area']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'symptom': 'itchy, flaky, slightly scaly',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': ReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'affected area': FieldReAsk(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m incorrect_value='beard, eyebrows & nares',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value beard, eyebrows & nares is not in choices ['head', 'neck', \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fail_results=[\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m FailResult(\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m outcome='fail',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m metadata=None,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m error_message=\"Value beard, eyebrows & nares is not in choices ['head', \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m'neck', 'chest'].\",\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m fix_value=None\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m path=['patient_info', 'symptoms', 1, 'affected area']\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m )\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'current_meds': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'medication': 'OTC steroid cream', 'response': 'Moderate'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'medication': 'OTC steroid cream',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'response': 'Moderate response'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", @@ -1128,13 +1303,17 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"affected area\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"face & hair\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"Value face & hair is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"Value face & hair is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"affected area\": {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"incorrect_value\": \"beard, eyebrows & nares\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_message\": \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"error_messages\": [\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest'].\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1165,14 +1344,32 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mrequests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255menter `null`.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'foo': 'example one'}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255metc.]}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m- `` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mJson Output:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╭─\u001b[0m\u001b[48;2;255;240;242m────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m Instructions \u001b[0m\u001b[48;2;255;240;242m─────────────────────────────────────────────\u001b[0m\u001b[48;2;255;240;242m─╮\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mYou are a helpful assistant only capable of communicating with valid JSON, and no other text.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m`name` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mrequests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242menter `null`.\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242mHere are examples of simple (XML, JSON) pairs that show the expected behavior:\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'foo': 'example one'}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242metc.]}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m- `` => `{'baz': {'foo': 'Some String', 'index': 1}}`\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m│\u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m \u001b[0m\u001b[48;2;255;240;242m│\u001b[0m │\n", + " │ \u001b[48;2;255;240;242m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╭─\u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m Message History \u001b[0m\u001b[48;2;231;223;235m───────────────────────────────────────────\u001b[0m\u001b[48;2;231;223;235m─╮\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m│\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235mNo message history.\u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m \u001b[0m\u001b[48;2;231;223;235m│\u001b[0m │\n", + " │ \u001b[48;2;231;223;235m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m │\n", " │ \u001b[48;2;245;245;220m╭─\u001b[0m\u001b[48;2;245;245;220m───────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m Raw LLM Output \u001b[0m\u001b[48;2;245;245;220m────────────────────────────────────────────\u001b[0m\u001b[48;2;245;245;220m─╮\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"patient_info\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", @@ -1200,7 +1397,10 @@ " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'current_meds': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'medication': 'OTC steroid cream', 'response': 'Moderate'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'medication': 'OTC steroid cream',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'response': 'Moderate response'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", @@ -1233,7 +1433,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.4" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/getting_started.rail b/docs/guardrails_ai/getting_started.rail similarity index 96% rename from docs/getting_started.rail rename to docs/guardrails_ai/getting_started.rail index a666b2f73..5f5f8c001 100644 --- a/docs/getting_started.rail +++ b/docs/guardrails_ai/getting_started.rail @@ -33,8 +33,8 @@ Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's information. -{{doctors_notes}} +${doctors_notes} -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2}
\ No newline at end of file diff --git a/docs/guardrails_ai/installation.md b/docs/guardrails_ai/installation.md new file mode 100644 index 000000000..87758f66d --- /dev/null +++ b/docs/guardrails_ai/installation.md @@ -0,0 +1,51 @@ +# Installing GuardRails AI + +Guardrails AI runs anywhere your python app runs. It is a simple pip install away. + +```bash +pip install guardrails-ai +``` + +## Releases + +Currently in beta, GuardRails AI maintains both stable and pre-release versions. + +Different versions can be found in the PyPi Release History: +https://pypi.org/project/guardrails-ai/#history + + +### Install Pre-Release Version +To install the latest, experimental pre-released version, run: + +```bash +pip install --pre guardrails-ai +``` + +### Install specific version +To install a specific version, run: + +```bash +# pip install guardrails-ai==[version-number] + +# Example: +pip install guardrails-ai==0.2.0a6 +``` + +## Install from GitHub + +Installing directly from GitHub is useful when a release has not yet been cut with the changes pushed to a branch that you need. Non-released versions may include breaking changes, and may not yet have full test coverage. We recommend using a released version whenever possible. + +```bash +# pip install git+https://github.com/ShreyaR/guardrails.git@[branch/commit/tag] +# Examples: +pip install git+https://github.com/ShreyaR/guardrails.git@main +pip install git+https://github.com/ShreyaR/guardrails.git@dev +``` + +## Recommended Python Dependency Versions + +The GuardRails package doesn't support pydantic versions 2.0.0 and above. We recommend using pydantic version 1.10.9. + +```bash +pip install pydantic==1.10.9 +``` \ No newline at end of file diff --git a/docs/guardrails_ai/introduction.md b/docs/guardrails_ai/introduction.md new file mode 100644 index 000000000..7964310ef --- /dev/null +++ b/docs/guardrails_ai/introduction.md @@ -0,0 +1,51 @@ +# Guardrails.ai + +_Note: Guardrails is an alpha release, so expect sharp edges and bugs._ + +## 🛤️ What is Guardrails? + +Guardrails is a Python package that lets a user add structure, type and quality guarantees to the outputs of large language models (LLMs). Guardrails: + +- does pydantic-style validation of LLM outputs. This includes semantic validation such as checking for bias in generated text, checking for bugs in generated code, etc. +- takes corrective actions (e.g. reasking LLM) when validation fails, +- enforces structure and type guarantees (e.g. JSON). + +## 🚒 Under the hood + +Guardrails provides a format (`.rail`) for enforcing a specification on an LLM output, and a lightweight wrapper around LLM API calls to implement this spec. + +1. `rail` (**R**eliable **AI** markup **L**anguage) files for specifying structure and type information, validators and corrective actions over LLM outputs. +2. `gd.Guard` wraps around LLM API calls to structure, validate and correct the outputs. + +``` mermaid +graph LR + A[Create `RAIL` spec] --> B["Initialize `guard` from spec"]; + B --> C["Wrap LLM API call with `guard`"]; +``` + +Check out the [Getting Started](getting_started.ipynb) guide to learn how to use Guardrails. + +### 📜 `RAIL` spec + +At the heart of Guardrails is the `rail` spec. `rail` is intended to be a language-agnostic, human-readable format for specifying structure and type information, validators and corrective actions over LLM outputs. + +`rail` is a flavor of XML that lets users specify: + +1. The expected structure and types of the LLM output (e.g. JSON), +2. The quality criteria for the output to be considered valid (e.g. generated text should be bias-free, generated code should be bug-free), +3. Corrective actions to be taken if the output is invalid (e.g. reask the LLM, filter out the invalid output, etc.) + +To learn more about the `rail` spec and the design decisions behind it, check out the [Rail Specification](rail/index.md). To learn how to write your own `rail` spec, check out [specifying `output` elements in RAIL](rail/output.md). + +## 📍 Roadmap + +- [ ] Adding more examples, new use cases and domains +- [x] Adding integrations with langchain, gpt-index, minichain, manifest +- [ ] Expanding validators offering +- [ ] More compilers from `.rail` -> LLM prompt (e.g. `.rail` -> TypeScript) +- [ ] Informative logging +- [x] Improving reasking logic +- [ ] A guardrails.js implementation +- [ ] VSCode extension for `.rail` files +- [ ] Next version of `.rail` format +- [x] Add more LLM providers diff --git a/docs/integrations/langchain.ipynb b/docs/integrations/langchain.ipynb index 9de84ff82..c220f8227 100644 --- a/docs/integrations/langchain.ipynb +++ b/docs/integrations/langchain.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:44.813776Z", @@ -62,9 +62,9 @@ "\n", "Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's information.\n", "\n", - "{{doctors_notes}}\n", + "${doctors_notes}\n", "\n", - "@complete_json_suffix_v2\n", + "${gr.complete_json_suffix_v2}\n", "\n", "\n", "\"\"\"" @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:46.123745Z", @@ -98,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:47.843397Z", @@ -119,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:49.828250Z", @@ -135,7 +135,7 @@ "Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's \n", "information.\n", "\n", - "{doctors_notes}\n", + "${doctors_notes}\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", @@ -155,10 +155,10 @@ "specific types. Be correct and concise.\n", "\n", "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n", - "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n", - "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n", + "- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\n", + "- `<list name='bar'><string format='upper-case' /></list>` => `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\n", "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n", - "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n", + "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", "\n", "\n", "\n" @@ -169,7 +169,7 @@ "Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's \n", "information.\n", "\n", - "\u001b[1m{\u001b[0mdoctors_notes\u001b[1m}\u001b[0m\n", + "$\u001b[1m{\u001b[0mdoctors_notes\u001b[1m}\u001b[0m\n", "\n", "\n", "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", @@ -189,10 +189,10 @@ "\u001b[39mspecific types. Be correct and concise.\u001b[0m\n", "\n", "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", + "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", + "\u001b[35m/\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", "\n", "\n" ] @@ -202,14 +202,14 @@ } ], "source": [ - "print(output_parser.guard.base_prompt)" + "print(output_parser.guard.prompt)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can now create a LangChain `PromptTemplate` from this output parser." + "We can now create a LangChain `PromptTemplate` from this output parser. Note that the `PromptTemplate` class from LangChain utilizes f-strings. In order to prevent it from trying to treat our example json as variables that should be substituted we will escape our prompt." ] }, { @@ -221,7 +221,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:54.395309Z", @@ -231,7 +231,7 @@ "outputs": [], "source": [ "prompt = PromptTemplate(\n", - " template=output_parser.guard.base_prompt,\n", + " template=output_parser.guard.prompt.escape(),\n", " input_variables=output_parser.guard.prompt.variable_names,\n", ")" ] @@ -245,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2023-06-21T16:39:57.246325Z", @@ -266,9 +266,16 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 8, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" + ] + }, { "data": { "text/html": [ @@ -276,8 +283,7 @@ " 'patient_info': {\n", " 'gender': 'Male',\n", " 'age': 49,\n", - " 'symptoms': 'Chronic macular rash to face & hair, worse in beard, eyebrows & nares. Itchy, flaky, slightly \n", - "scaly. Moderate response to OTC steroid cream'\n", + " 'symptoms': 'Itchy, flaky, slightly scaly. Moderate response to OTC steroid cream'\n", " }\n", "}\n", "\n" @@ -287,8 +293,7 @@ " \u001b[32m'patient_info'\u001b[0m: \u001b[1m{\u001b[0m\n", " \u001b[32m'gender'\u001b[0m: \u001b[32m'Male'\u001b[0m,\n", " \u001b[32m'age'\u001b[0m: \u001b[1;36m49\u001b[0m,\n", - " \u001b[32m'symptoms'\u001b[0m: \u001b[32m'Chronic macular rash to face & hair, worse in beard, eyebrows & nares. Itchy, flaky, slightly \u001b[0m\n", - "\u001b[32mscaly. Moderate response to OTC steroid cream'\u001b[0m\n", + " \u001b[32m'symptoms'\u001b[0m: \u001b[32m'Itchy, flaky, slightly scaly. Moderate response to OTC steroid cream'\u001b[0m\n", " \u001b[1m}\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -318,7 +323,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/docs/integrations/llm_api_wrappers.md b/docs/integrations/llm_api_wrappers.md new file mode 100644 index 000000000..7a2c2a165 --- /dev/null +++ b/docs/integrations/llm_api_wrappers.md @@ -0,0 +1,133 @@ +# Use Guardrails with LLM APIs + +Guardrails' `Guard` wrappers provide a simple way to add Guardrails to your LLM API calls. The wrappers are designed to be used with any LLM API. + + +Here are some examples of how to use the wrappers with different LLM providers and models: + +## OpenAI + +### Completion Models (e.g. GPT-3) + +```python +import openai +import guardrails as gd + + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Wrap openai API call +raw_llm_output, guardrail_output = guard( + openai.Completion.create, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + engine="text-davinci-003", + max_tokens=100, + temperature=0.0, +) +``` + +### ChatCompletion Models (e.g. ChatGPT) + +```python +import openai +import guardrails as gd + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Wrap openai API call +raw_llm_output, guardrail_output = guard( + openai.ChatCompletion.create, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + system_prompt="You are a helpful assistant...", + model="gpt-3.5-turbo", + max_tokens=100, + temperature=0.0, +) +``` + +## Cohere + +### Generate (e.g. GPT-3) + +```python +import cohere +import guardrails as gd + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Create a Cohere client +cohere_client = cohere.Client(api_key="my_api_key") + +# Wrap cohere API call +raw_llm_output, guardrail_output = guard( + cohere_client.generate, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + model="command-nightly", + max_tokens=100, + ... +) +``` + +## Using Manifest +[Manifest](https://github.com/HazyResearch/manifest) is a wrapper around most model APIs and supports hosting local models. It can be used as a LLM API. + +```python +import guardrails as gd +import manifest + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Create a Manifest client - this one points to GPT-4 +# and caches responses in SQLLite +manifest = manifest.Manifest( + client_name="openai", + engine="gpt-4", + cache_name="sqlite", + cache_connection="my_manifest_cache.db" +) + +# Wrap openai API call +raw_llm_output, guardrail_output = guard( + manifest, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + max_tokens=100, + temperature=0.0, +) +``` + + +## Using a custom LLM API + +```python +import guardrails as gd + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Function that takes the prompt as a string and returns the LLM output as string +def my_llm_api(prompt: str, **kwargs) -> str: + """Custom LLM API wrapper. + + Args: + prompt (str): The prompt to be passed to the LLM API + **kwargs: Any additional arguments to be passed to the LLM API + + Returns: + str: The output of the LLM API + """ + + # Call your LLM API here + return ... + + +# Wrap your LLM API call +raw_llm_output, guardrail_output = guard( + my_llm_api, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + **kwargs, +) +``` diff --git a/docs/integrations/pydantic_validation.ipynb b/docs/integrations/pydantic_validation.ipynb deleted file mode 100644 index 81d55adb9..000000000 --- a/docs/integrations/pydantic_validation.ipynb +++ /dev/null @@ -1,570 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Validating LLM Outputs with Pydantic\n", - "\n", - "!!! note\n", - " To download this example as a Jupyter notebook, click [here](https://github.com/ShreyaR/guardrails/blob/main/docs/integrations/pydantic_validation.ipynb).\n", - "\n", - "In this example, we will use Guardrails with Pydantic.\n", - "\n", - "## Objective\n", - "\n", - "We want to generate synthetic data that is consistent with a `Person` Pydantic BaseModel." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import guardrails as gd\n", - "\n", - "from rich import print" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 1: Create the RAIL Spec\n", - "\n", - "Ordinarily, we would create an RAIL spec in a separate file. For the purposes of this example, we will create the spec in this notebook as a string following the RAIL syntax. For more information on RAIL, see the [RAIL documentation](../rail/output.md).\n", - "\n", - "Here, we define a Pydantic model for a `Person` with the following fields:\n", - "\n", - "- `name`: a string \n", - "- `age`: an integer \n", - "- `zip_code`: a string zip code\n", - "\n", - "and write very simple validators for the fields as an example. As a way to show how LLM reasking can be used to generate data that is consistent with the Pydantic model, we can define a validator that asks for a zip code in California (including being perversely opposed to the \"90210\" zip code). If this validator fails, the LLM will be sent the error message and will reask the question.\n", - "\n", - "This Pydantic model could also be any model that you already have in your codebase, and just needs to be decorated with `@register_pydantic`.\n", - "\n", - "\n", - "To use this model in the `` specification, we used the special\n", - "`pydantic` tag. This tag takes the name of the Pydantic model, as well as the\n", - "`on-fail-pydantic` attribute, which specifies what to do when the output\n", - "does not validate against the Pydantic model." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "rail_str = \"\"\"\n", - "\n", - "\n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "Generate data for possible users in accordance with the specification below.\n", - "\n", - "@xml_prefix_prompt\n", - "\n", - "{output_schema}\n", - "\n", - "@complete_json_suffix_v2\n", - "\n", - "\n", - "\"\"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 2: Create a `Guard` object with the RAIL Spec\n", - "\n", - "We create a `gd.Guard` object that will check, validate and correct the output of the LLM. This object:\n", - "\n", - "1. Enforces the quality criteria specified in the RAIL spec.\n", - "2. Takes corrective action when the quality criteria are not met.\n", - "3. Compiles the schema and type info from the RAIL spec and adds it to the prompt." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "guard = gd.Guard.from_rail_string(rail_str)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see the prompt that will be sent to the LLM." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n",
-       "Generate data for possible users in accordance with the specification below.\n",
-       "\n",
-       "\n",
-       "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
-       "\n",
-       "\n",
-       "<output>\n",
-       "    <list name=\"people\" description=\"A list of 3 people.\">\n",
-       "        <object description=\"Information about a person.\" pydantic=\"Person\"><string name=\"name\" description=\"The \n",
-       "name of the person.\"/><integer name=\"age\" description=\"The age of the person.\" \n",
-       "format=\"age-must-be-between-0-and-150\"/><string name=\"zip_code\" description=\"The zip code of the person.\" \n",
-       "format=\"zip-code-must-be-numeric; zip-code-in-california\"/></object></list>\n",
-       "</output>\n",
-       "\n",
-       "\n",
-       "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n",
-       "\n",
-       "<output>\n",
-       "    <list name=\"people\" description=\"A list of 3 people.\">\n",
-       "        <object description=\"Information about a person.\" pydantic=\"Person\"><string name=\"name\" description=\"The \n",
-       "name of the person.\"/><integer name=\"age\" description=\"The age of the person.\" \n",
-       "format=\"age-must-be-between-0-and-150\"/><string name=\"zip_code\" description=\"The zip code of the person.\" \n",
-       "format=\"zip-code-must-be-numeric; zip-code-in-california\"/></object></list>\n",
-       "</output>\n",
-       "\n",
-       "ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` \n",
-       "attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\n",
-       "MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \n",
-       "specific types. Be correct and concise.\n",
-       "\n",
-       "Here are examples of simple (XML, JSON) pairs that show the expected behavior:\n",
-       "- `<string name='foo' format='two-words lower-case' />` => `{{'foo': 'example one'}}`\n",
-       "- `<list name='bar'><string format='upper-case' /></list>` => `{{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}}`\n",
-       "- `<object name='baz'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}`\n",
-       "\n",
-       "JSON Object:\n",
-       "
\n" - ], - "text/plain": [ - "\n", - "Generate data for possible users in accordance with the specification below.\n", - "\n", - "\n", - "Given below is XML that describes the information to extract from this document and the tags to extract it into.\n", - "\n", - "\n", - "\u001b[1m<\u001b[0m\u001b[1;95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\n", - "\n", - "\u001b[39mGiven below is XML that describes the information to extract from this document and the tags to extract it into.\u001b[0m\n", - "\n", - "\u001b[39m\u001b[0m\n", - "\u001b[39m \u001b[0m\n", - "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m><\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>\u001b[0m\n", - "\u001b[39m<\u001b[0m\u001b[35m/\u001b[0m\u001b[95moutput\u001b[0m\u001b[39m>\u001b[0m\n", - "\n", - "\u001b[39mONLY return a valid JSON object \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mno other text is necessary\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m, where the key of the field in JSON is the `name` \u001b[0m\n", - "\u001b[39mattribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON\u001b[0m\n", - "\u001b[39mMUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and \u001b[0m\n", - "\u001b[39mspecific types. Be correct and concise.\u001b[0m\n", - "\n", - "\u001b[39mHere are examples of simple \u001b[0m\u001b[1;39m(\u001b[0m\u001b[39mXML, JSON\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m pairs that show the expected behavior:\u001b[0m\n", - "\u001b[39m- `` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'foo'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'example one'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mlist\u001b[0m\u001b[39m>` => `\u001b[0m\u001b[1;39m{\u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m\"bar\"\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'STRING ONE'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'STRING TWO'\u001b[0m\u001b[39m, etc.\u001b[0m\u001b[1;39m]\u001b[0m\u001b[1;39m}\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m`\u001b[0m\n", - "\u001b[39m- `<\u001b[0m\u001b[35m/\u001b[0m\u001b[95mobject\u001b[0m\u001b[39m>` =\u001b[0m\u001b[1m>\u001b[0m `\u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'baz'\u001b[0m: \u001b[1m{\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'foo'\u001b[0m: \u001b[32m'Some String'\u001b[0m, \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m`\n", - "\n", - "JSON Object:\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(guard.base_prompt)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "!!! note\n", - " Notice that the prompt replaces the `pydantic` tag with the schema, validator and type information from the Pydantic model. This e.g. tells the LLM that we want that `zip-code-must-be-numeric` and `zip-code-in-california`. Guardrails will even automatically read the docstrings from the Pydantic model and add them to the prompt!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/krandiash/opt/anaconda3/envs/guardrails/lib/python3.9/site-packages/eliot/json.py:22: FutureWarning: In the future `np.bool` will be defined as the corresponding NumPy scalar.\n", - " if isinstance(o, (numpy.bool, numpy.bool_)):\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response = guard(\n", - " openai.Completion.create,\n", - " engine=\"text-davinci-003\",\n", - " max_tokens=512,\n", - " temperature=0.5,\n", - " num_reasks=2,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{\n",
-       "    'people': [\n",
-       "        Person(name='John Doe', age=25, zip_code='90000'),\n",
-       "        Person(name='Jane Doe', age=30, zip_code='94105'),\n",
-       "        Person(name='John Smith', age=40, zip_code='90001')\n",
-       "    ]\n",
-       "}\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1m{\u001b[0m\n", - " \u001b[32m'people'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'John Doe'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m25\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'90000'\u001b[0m\u001b[1m)\u001b[0m,\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'Jane Doe'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m30\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'94105'\u001b[0m\u001b[1m)\u001b[0m,\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'John Smith'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m40\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'90001'\u001b[0m\u001b[1m)\u001b[0m\n", - " \u001b[1m]\u001b[0m\n", - "\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(validated_response)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `guard` wrapper returns the raw_llm_respose (which is a simple string), and the validated and corrected output (which is a dictionary).\n", - "\n", - "We can see that the output is a dictionary with the correct schema and contains a few `Person` objects!\n", - "\n", - "We can even print out the logs of the most recent call. Notice that the first time the LLM actually returns a Beverly Hills zip code, the LLM is sent the error message and is reasked. The second time, the LLM returns a valid zip code and the output is returned." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
GuardHistory(\n",
-       "    history=[\n",
-       "        GuardLogs(\n",
-       "            prompt='\\nGenerate data for possible users in accordance with the specification below.\\n\\n\\nGiven below\n",
-       "is XML that describes the information to extract from this document and the tags to extract it \n",
-       "into.\\n\\n\\n<output>\\n    <list name=\"people\" description=\"A list of 3 people.\">\\n        <object \n",
-       "description=\"Information about a person.\" pydantic=\"Person\"><string name=\"name\" description=\"The name of the \n",
-       "person.\"/><integer name=\"age\" description=\"The age of the person.\" format=\"age-must-be-between-0-and-150\"/><string \n",
-       "name=\"zip_code\" description=\"The zip code of the person.\" format=\"zip-code-must-be-numeric; \n",
-       "zip-code-in-california\"/></object></list>\\n</output>\\n\\n\\nGiven below is XML that describes the information to \n",
-       "extract from this document and the tags to extract it into.\\n\\n<output>\\n    <list name=\"people\" description=\"A \n",
-       "list of 3 people.\">\\n        <object description=\"Information about a person.\" pydantic=\"Person\"><string \n",
-       "name=\"name\" description=\"The name of the person.\"/><integer name=\"age\" description=\"The age of the person.\" \n",
-       "format=\"age-must-be-between-0-and-150\"/><string name=\"zip_code\" description=\"The zip code of the person.\" \n",
-       "format=\"zip-code-must-be-numeric; zip-code-in-california\"/></object></list>\\n</output>\\n\\nONLY return a valid JSON \n",
-       "object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the \n",
-       "corresponding XML, and the value is of the type specified by the corresponding XML\\'s tag. The JSON MUST conform to\n",
-       "the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be \n",
-       "correct and concise.\\n\\nHere are examples of simple (XML, JSON) pairs that show the expected behavior:\\n- `<string \n",
-       "name=\\'foo\\' format=\\'two-words lower-case\\' />` => `{\\'foo\\': \\'example one\\'}`\\n- `<list name=\\'bar\\'><string \n",
-       "format=\\'upper-case\\' /></list>` => `{\"bar\": [\\'STRING ONE\\', \\'STRING TWO\\', etc.]}`\\n- `<object \n",
-       "name=\\'baz\\'><string name=\"foo\" format=\"capitalize two-words\" /><integer name=\"index\" format=\"1-indexed\" \n",
-       "/></object>` => `{\\'baz\\': {\\'foo\\': \\'Some String\\', \\'index\\': 1}}`\\n\\nJSON Object:',\n",
-       "            output=' \\n{\\n    \"people\": [\\n        {\\n            \"name\": \"John Doe\",\\n            \"age\": 25,\\n    \n",
-       "\"zip_code\": \"90210\"\\n        },\\n        {\\n            \"name\": \"Jane Doe\",\\n            \"age\": 30,\\n            \n",
-       "\"zip_code\": \"94105\"\\n        },\\n        {\\n            \"name\": \"John Smith\",\\n            \"age\": 40,\\n            \n",
-       "\"zip_code\": \"90001\"\\n        }\\n    ]\\n}',\n",
-       "            output_as_dict={\n",
-       "                'people': [\n",
-       "                    {'name': 'John Doe', 'age': 25, 'zip_code': '90210'},\n",
-       "                    {'name': 'Jane Doe', 'age': 30, 'zip_code': '94105'},\n",
-       "                    {'name': 'John Smith', 'age': 40, 'zip_code': '90001'}\n",
-       "                ]\n",
-       "            },\n",
-       "            validated_output={\n",
-       "                'people': [\n",
-       "                    {\n",
-       "                        'name': 'John Doe',\n",
-       "                        'age': 25,\n",
-       "                        'zip_code': ReAsk(\n",
-       "                            incorrect_value='90210',\n",
-       "                            error_message='Zip code must not be Beverly Hills.',\n",
-       "                            fix_value=None,\n",
-       "                            path=['people', 0]\n",
-       "                        )\n",
-       "                    },\n",
-       "                    Person(name='Jane Doe', age=30, zip_code='94105'),\n",
-       "                    Person(name='John Smith', age=40, zip_code='90001')\n",
-       "                ]\n",
-       "            },\n",
-       "            reasks=[\n",
-       "                ReAsk(\n",
-       "                    incorrect_value='90210',\n",
-       "                    error_message='Zip code must not be Beverly Hills.',\n",
-       "                    fix_value=None,\n",
-       "                    path=['people', 0]\n",
-       "                )\n",
-       "            ]\n",
-       "        ),\n",
-       "        GuardLogs(\n",
-       "            prompt='\\nI was given the following JSON response, which had problems due to incorrect values.\\n\\n{\\n  \n",
-       "\"people\": [\\n    {\\n      \"name\": \"John Doe\",\\n      \"age\": 25,\\n      \"zip_code\": {\\n        \"incorrect_value\": \n",
-       "\"90210\",\\n        \"error_message\": \"Zip code must not be Beverly Hills.\"\\n      }\\n    }\\n  ]\\n}\\n\\nHelp me correct\n",
-       "the incorrect values based on the given error messages.\\n\\nGiven below is XML that describes the information to \n",
-       "extract from this document and the tags to extract it into.\\n\\n<output>\\n    <list name=\"people\" description=\"A \n",
-       "list of 3 people.\">\\n        <object description=\"Information about a person.\" pydantic=\"Person\"><string \n",
-       "name=\"name\" description=\"The name of the person.\"/><integer name=\"age\" description=\"The age of the person.\" \n",
-       "format=\"age-must-be-between-0-and-150\"/><string name=\"zip_code\" description=\"The zip code of the person.\" \n",
-       "format=\"zip-code-must-be-numeric; zip-code-in-california\"/></object></list>\\n</output>\\n\\nONLY return a valid JSON \n",
-       "object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the \n",
-       "corresponding XML, and the value is of the type specified by the corresponding XML\\'s tag. The JSON MUST conform to\n",
-       "the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be \n",
-       "correct and concise. If you are unsure anywhere, enter `null`.\\n\\nHere are examples of simple (XML, JSON) pairs \n",
-       "that show the expected behavior:\\n- `<string name=\\'foo\\' format=\\'two-words lower-case\\' />` => `{{\\'foo\\': \n",
-       "\\'example one\\'}}`\\n- `<list name=\\'bar\\'><string format=\\'upper-case\\' /></list>` => `{{\"bar\": [\\'STRING ONE\\', \n",
-       "\\'STRING TWO\\', etc.]}}`\\n- `<object name=\\'baz\\'><string name=\"foo\" format=\"capitalize two-words\" /><integer \n",
-       "name=\"index\" format=\"1-indexed\" /></object>` => `{{\\'baz\\': {{\\'foo\\': \\'Some String\\', \\'index\\': 1}}}}`\\n\\nJSON \n",
-       "Object:',\n",
-       "            output='\\n{\\n    \"people\": [\\n        {\\n            \"name\": \"John Doe\",\\n            \"age\": 25,\\n     \n",
-       "\"zip_code\": \"90000\"\\n        }\\n    ]\\n}',\n",
-       "            output_as_dict={'people': [{'name': 'John Doe', 'age': 25, 'zip_code': '90000'}]},\n",
-       "            validated_output={\n",
-       "                'people': [\n",
-       "                    Person(name='John Doe', age=25, zip_code='90000'),\n",
-       "                    Person(name='Jane Doe', age=30, zip_code='94105'),\n",
-       "                    Person(name='John Smith', age=40, zip_code='90001')\n",
-       "                ]\n",
-       "            },\n",
-       "            reasks=[]\n",
-       "        )\n",
-       "    ]\n",
-       ")\n",
-       "
\n" - ], - "text/plain": [ - "\u001b[1;35mGuardHistory\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mhistory\u001b[0m=\u001b[1m[\u001b[0m\n", - " \u001b[1;35mGuardLogs\u001b[0m\u001b[1m(\u001b[0m\n", - " \u001b[33mprompt\u001b[0m=\u001b[32m'\\nGenerate data for possible users in accordance with the specification below.\\n\\n\\nGiven below\u001b[0m\n", - "\u001b[32mis XML that describes the information to extract from this document and the tags to extract it \u001b[0m\n", - "\u001b[32minto.\\n\\n\\n\u001b[0m\u001b[32m<\u001b[0m\u001b[32moutput\u001b[0m\u001b[32m>\\n \\n \\n\\n\\n\\nGiven below is XML that describes the information to \u001b[0m\n", - "\u001b[32mextract from this document and the tags to extract it into.\\n\\n\\n \\n \\n\\n\\nONLY return a valid JSON \u001b[0m\n", - "\u001b[32mobject \u001b[0m\u001b[32m(\u001b[0m\u001b[32mno other text is necessary\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, where the key of the field in JSON is the `name` attribute of the \u001b[0m\n", - "\u001b[32mcorresponding XML, and the value is of the type specified by the corresponding XML\\'s tag. The JSON MUST conform to\u001b[0m\n", - "\u001b[32mthe XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be \u001b[0m\n", - "\u001b[32mcorrect and concise.\\n\\nHere are examples of simple \u001b[0m\u001b[32m(\u001b[0m\u001b[32mXML, JSON\u001b[0m\u001b[32m)\u001b[0m\u001b[32m pairs that show the expected behavior:\\n- `` => `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'foo\\': \\'example one\\'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n- `` => `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"bar\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\'STRING ONE\\', \\'STRING TWO\\', etc.\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n- `` => `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'baz\\': \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'foo\\': \\'Some String\\', \\'index\\': 1\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n\\nJSON Object:'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33moutput\u001b[0m\u001b[39m=\u001b[0m\u001b[32m' \\n\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"people\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"name\": \"John Doe\",\\n \"age\": 25,\\n \u001b[0m\n", - "\u001b[32m\"zip_code\": \"90210\"\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m,\\n \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"name\": \"Jane Doe\",\\n \"age\": 30,\\n \u001b[0m\n", - "\u001b[32m\"zip_code\": \"94105\"\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m,\\n \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"name\": \"John Smith\",\\n \"age\": 40,\\n \u001b[0m\n", - "\u001b[32m\"zip_code\": \"90001\"\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\n\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33moutput_as_dict\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[32m'people'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'name'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'John Doe'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'age'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m25\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'zip_code'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'90210'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'name'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'Jane Doe'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'age'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m30\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'zip_code'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'94105'\u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m{\u001b[0m\u001b[32m'name'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'John Smith'\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'age'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m40\u001b[0m\u001b[39m, \u001b[0m\u001b[32m'zip_code'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'90001'\u001b[0m\u001b[1;39m}\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m]\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mvalidated_output\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[32m'people'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[32m'name'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'John Doe'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[32m'age'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;36m25\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[32m'zip_code'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;35mReAsk\u001b[0m\u001b[1;39m(\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mincorrect_value\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'90210'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33merror_message\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'Zip code must not be Beverly Hills.'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mfix_value\u001b[0m\u001b[39m=\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mpath\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'people'\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;39m]\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m)\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;35mPerson\u001b[0m\u001b[1;39m(\u001b[0m\u001b[33mname\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'Jane Doe'\u001b[0m\u001b[39m, \u001b[0m\u001b[33mage\u001b[0m\u001b[39m=\u001b[0m\u001b[1;36m30\u001b[0m\u001b[39m, \u001b[0m\u001b[33mzip_code\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'94105'\u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;35mPerson\u001b[0m\u001b[1;39m(\u001b[0m\u001b[33mname\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'John Smith'\u001b[0m\u001b[39m, \u001b[0m\u001b[33mage\u001b[0m\u001b[39m=\u001b[0m\u001b[1;36m40\u001b[0m\u001b[39m, \u001b[0m\u001b[33mzip_code\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'90001'\u001b[0m\u001b[1;39m)\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m]\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mreasks\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;35mReAsk\u001b[0m\u001b[1;39m(\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mincorrect_value\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'90210'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33merror_message\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'Zip code must not be Beverly Hills.'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mfix_value\u001b[0m\u001b[39m=\u001b[0m\u001b[3;35mNone\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mpath\u001b[0m\u001b[39m=\u001b[0m\u001b[1;39m[\u001b[0m\u001b[32m'people'\u001b[0m\u001b[39m, \u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;39m]\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m)\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m]\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;39m)\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[1;35mGuardLogs\u001b[0m\u001b[1;39m(\u001b[0m\n", - "\u001b[39m \u001b[0m\u001b[33mprompt\u001b[0m\u001b[39m=\u001b[0m\u001b[32m'\\nI was given the following JSON response, which had problems due to incorrect values.\\n\\n\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \u001b[0m\n", - "\u001b[32m\"people\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"name\": \"John Doe\",\\n \"age\": 25,\\n \"zip_code\": \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"incorrect_value\": \u001b[0m\n", - "\u001b[32m\"90210\",\\n \"error_message\": \"Zip code must not be Beverly Hills.\"\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\n\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\n\\nHelp me correct\u001b[0m\n", - "\u001b[32mthe incorrect values based on the given error messages.\\n\\nGiven below is XML that describes the information to \u001b[0m\n", - "\u001b[32mextract from this document and the tags to extract it into.\\n\\n\\n \\n \\n\\n\\nONLY return a valid JSON \u001b[0m\n", - "\u001b[32mobject \u001b[0m\u001b[32m(\u001b[0m\u001b[32mno other text is necessary\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, where the key of the field in JSON is the `name` attribute of the \u001b[0m\n", - "\u001b[32mcorresponding XML, and the value is of the type specified by the corresponding XML\\'s tag. The JSON MUST conform to\u001b[0m\n", - "\u001b[32mthe XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be \u001b[0m\n", - "\u001b[32mcorrect and concise. If you are unsure anywhere, enter `null`.\\n\\nHere are examples of simple \u001b[0m\u001b[32m(\u001b[0m\u001b[32mXML, JSON\u001b[0m\u001b[32m)\u001b[0m\u001b[32m pairs \u001b[0m\n", - "\u001b[32mthat show the expected behavior:\\n- `` => `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'foo\\': \u001b[0m\n", - "\u001b[32m\\'example one\\'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n- `` => `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"bar\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\'STRING ONE\\', \u001b[0m\n", - "\u001b[32m\\'STRING TWO\\', etc.\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n- `` =\u001b[0m\u001b[32m>\u001b[0m\u001b[32m `\u001b[0m\u001b[32m{\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'baz\\': \u001b[0m\u001b[32m{\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\'foo\\': \\'Some String\\', \\'index\\': 1\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m`\\n\\nJSON \u001b[0m\n", - "\u001b[32mObject:'\u001b[0m,\n", - " \u001b[33moutput\u001b[0m=\u001b[32m'\\n\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"people\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\n \"name\": \"John Doe\",\\n \"age\": 25,\\n \u001b[0m\n", - "\u001b[32m\"zip_code\": \"90000\"\\n \u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\n \u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\n\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - " \u001b[33moutput_as_dict\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'people'\u001b[0m: \u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'name'\u001b[0m: \u001b[32m'John Doe'\u001b[0m, \u001b[32m'age'\u001b[0m: \u001b[1;36m25\u001b[0m, \u001b[32m'zip_code'\u001b[0m: \u001b[32m'90000'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[33mvalidated_output\u001b[0m=\u001b[1m{\u001b[0m\n", - " \u001b[32m'people'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'John Doe'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m25\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'90000'\u001b[0m\u001b[1m)\u001b[0m,\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'Jane Doe'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m30\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'94105'\u001b[0m\u001b[1m)\u001b[0m,\n", - " \u001b[1;35mPerson\u001b[0m\u001b[1m(\u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'John Smith'\u001b[0m, \u001b[33mage\u001b[0m=\u001b[1;36m40\u001b[0m, \u001b[33mzip_code\u001b[0m=\u001b[32m'90001'\u001b[0m\u001b[1m)\u001b[0m\n", - " \u001b[1m]\u001b[0m\n", - " \u001b[1m}\u001b[0m,\n", - " \u001b[33mreasks\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - " \u001b[1m)\u001b[0m\n", - " \u001b[1m]\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(guard.state.most_recent_call)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "guardrails", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.16" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/llm_api_wrappers.md b/docs/llm_api_wrappers.md index f07b76622..7a2c2a165 100644 --- a/docs/llm_api_wrappers.md +++ b/docs/llm_api_wrappers.md @@ -47,6 +47,30 @@ raw_llm_output, guardrail_output = guard( ) ``` +## Cohere + +### Generate (e.g. GPT-3) + +```python +import cohere +import guardrails as gd + +# Create a Guard class +guard = gd.Guard.from_rail(...) + +# Create a Cohere client +cohere_client = cohere.Client(api_key="my_api_key") + +# Wrap cohere API call +raw_llm_output, guardrail_output = guard( + cohere_client.generate, + prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, + model="command-nightly", + max_tokens=100, + ... +) +``` + ## Using Manifest [Manifest](https://github.com/HazyResearch/manifest) is a wrapper around most model APIs and supports hosting local models. It can be used as a LLM API. diff --git a/docs/logs.md b/docs/logs.md deleted file mode 100644 index a9adec5ec..000000000 --- a/docs/logs.md +++ /dev/null @@ -1,32 +0,0 @@ -# Inspecting logs - -All `gd.Guard` calls are logged internally, and can be accessed via two methods, `gd.Guard.guard_state` or `guardrails.log`. - -## 🪵 Accessing logs via `guardrails.log` - -This is the simplest way to access logs. It returns a list of all `gd.Guard` calls, in the order they were made. - -In order to access logs, run: - -```bash - -eliot-tree --output-format=ascii guardrails.log - -``` - -## 🇻🇦 Accessing logs via `gd.Guard.guard_state` - -`guard_state` is an attribute of the `gd.Guard` class. It contains: - -1. A list of all `gd.Guard` calls, in the order they were made. -2. For each call, reasks needed and their results. - -In order to access logs, run: - -```python -from rich import print - -print(guard.state.most_recent_call.tree) -``` - -![guard_state](img/guard_history.png) diff --git a/docs/rail/script.md b/docs/rail/script.md deleted file mode 100644 index 5dfd88520..000000000 --- a/docs/rail/script.md +++ /dev/null @@ -1,82 +0,0 @@ -# `Script` Element - -!!! note - This is a beta feature, and serves more advanced use cases. If you're just getting started with Guardrails, you can skip this section for now. - -The `` element contains any custom code that a developer wants to use. Common use cases include: - -1. Custom `Validators`: Here's a few examples of adding custom validators via the ` - -``` - -## 🔐 Adding a custom `Validator` - -Here's an example of adding a custom validator to check if a generated text contains any secret keys. The validator is added via the ` - -``` - -1. In order to add a custom validator, you need to import the `Validator` class, `EventDetail` class, and `register_validator` decorator. -2. Add the `register_validator` decorator to your custom validator class. The `name` argument is the name of the validator (this will be used in `RAIL` as the formatter name), and the `data_type` argument is the data type that the validator is applicable to. In this case, the validator is applicable to strings. -3. Subclass the `Validator` class. -4. You only need to implement the `validate` method. The `validate` method takes in the `key`, `value`, and `schema` as arguments. The `key` is the key of the value in the JSON object, the `value` is the value itself, and the `schema` is the schema of the value. -5. The `validate` method raises an `EventDetail` object if the value is invalid. This object is then used to take corrective action specified in the `RAIL` spec. -6. The `validate` method should return the `schema` if the value is valid. - -The custom validator defined in above can be used in the `RAIL` spec as follows: - -```xml - - - - - -``` - -## 🧭 Adding a custom `DataType` - -Coming soon! diff --git a/docs/recipe/generating_structured_data.ipynb b/docs/recipe/generating_structured_data.ipynb new file mode 100644 index 000000000..984c5d8b1 --- /dev/null +++ b/docs/recipe/generating_structured_data.ipynb @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Hi" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/recipe/validating_string_data.ipynb b/docs/recipe/validating_string_data.ipynb new file mode 100644 index 000000000..984c5d8b1 --- /dev/null +++ b/docs/recipe/validating_string_data.ipynb @@ -0,0 +1,21 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Hi" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/guardrails/applications/text2sql.py b/guardrails/applications/text2sql.py index b478acd19..3ab788baf 100644 --- a/guardrails/applications/text2sql.py +++ b/guardrails/applications/text2sql.py @@ -15,24 +15,24 @@ REASK_PROMPT = """ You are a data scientist whose job is to write SQL queries. -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2} Here's schema about the database that you can use to generate the SQL query. Try to avoid using joins if the data can be retrieved from the same table. -{{db_info}} +${db_info} I will give you a list of examples. -{{examples}} +${examples} I want to create a query for the following instruction: -{{nl_instruction}} +${nl_instruction} For this instruction, I was given the following JSON, which has some incorrect values. -{previous_response} +${previous_response} Help me correct the incorrect values based on the given error messages. """ diff --git a/guardrails/applications/text2sql.rail b/guardrails/applications/text2sql.rail index 5bfd8edf0..27732b5df 100644 --- a/guardrails/applications/text2sql.rail +++ b/guardrails/applications/text2sql.rail @@ -12,7 +12,7 @@ You are a data scientist whose job is to write SQL queries. -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2} @@ -20,15 +20,15 @@ You are a data scientist whose job is to write SQL queries. Here's schema about the database that you can use to generate the SQL query. Try to avoid using joins if the data can be retrieved from the same table. -{{db_info}} +${db_info} I will give you a list of examples. Write a SQL query similar to the examples below: -{{examples}} +${examples} INSTRUCTIONS: --------- -{{nl_instruction}} +${nl_instruction} QUERY: --------- diff --git a/guardrails/constants.xml b/guardrails/constants.xml index b4dc26d13..19f3b4a9a 100644 --- a/guardrails/constants.xml +++ b/guardrails/constants.xml @@ -23,7 +23,7 @@ ONLY return a valid JSON object (no other text is necessary). The JSON MUST conf I was given the following JSON response, which had problems due to incorrect values. -{previous_response} +${previous_response} Help me correct the incorrect values based on the given error messages. @@ -31,7 +31,7 @@ Help me correct the incorrect values based on the given error messages. I was given the following JSON response, which had problems due to incorrect values. -{previous_response} +${previous_response} Help me correct the incorrect values based on the given error messages. @@ -39,7 +39,7 @@ Help me correct the incorrect values based on the given error messages. Given below is XML that describes the information to extract from this document and the tags to extract it into. -{output_schema} +${output_schema} ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. @@ -47,41 +47,41 @@ ONLY return a valid JSON object (no other text is necessary), where the key of t Given below is XML that describes the information to extract from this document and the tags to extract it into. -{output_schema} +${output_schema} ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- ``]]> => `{{{{'foo': 'example one'}}}}` -- `]]>` => `{{{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}}}` -- `
]]>` => `{{{{'baz': {{{{'foo': 'Some String', 'index': 1}}}}}}}}` +- ``]]> => `{'foo': 'example one'}` +- `
]]>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `
]]>` => `{'baz': {'foo': 'Some String', 'index': 1}}` Given below is XML that describes the information to extract from this document and the tags to extract it into. -{output_schema} +${output_schema} ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- ``]]> => `{{{{'foo': 'example one'}}}}` -- `
]]>` => `{{{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}}}` -- `
]]>` => `{{{{'baz': {{{{'foo': 'Some String', 'index': 1}}}}}}}}` +- ``]]> => `{'foo': 'example one'}` +- `
]]>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `
]]>` => `{'baz': {'foo': 'Some String', 'index': 1}}` Given below is XML that describes the information to extract from this document and the tags to extract it into. -{output_schema} +${output_schema} ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, try your best guess. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- ``]]> => `{{{{'foo': 'example one'}}}}` -- `
]]>` => `{{{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}}}` -- `
]]>` => `{{{{'baz': {{{{'foo': 'Some String', 'index': 1}}}}}}}}` +- ``]]> => `{'foo': 'example one'}` +- `
]]>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `
]]>` => `{'baz': {'foo': 'Some String', 'index': 1}}` @@ -89,24 +89,23 @@ Here are examples of simple (XML, JSON) pairs that show the expected behavior: ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- ``]]> => `{{{{'foo': 'example one'}}}}` -- `
]]>` => `{{{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}}}` -- `
]]>` => `{{{{'baz': {{{{'foo': 'Some String', 'index': 1}}}}}}}}` +- ``]]> => `{'foo': 'example one'}` +- `
]]>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `
]]>` => `{'baz': {'foo': 'Some String', 'index': 1}}` - -I was given the following string, delimited by +++: +This was a previous response you generated: -+++ -{previous_response} -+++ +====== +${previous_response} +====== -It had the following problems: -{error_messages} +Generate a new response that corrects your old response such that the following issues are fixed +${error_messages} -{output_schema} +${output_schema} @@ -115,9 +114,9 @@ You are a helpful assistant only capable of communicating with valid JSON, and n ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- ``]]> => `{{{{'foo': 'example one'}}}}` -- `]]>` => `{{{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}}}` -- `
]]>` => `{{{{'baz': {{{{'foo': 'Some String', 'index': 1}}}}}}}}` +- ``]]> => `{'foo': 'example one'}` +- `
]]>` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `
]]>` => `{'baz': {'foo': 'Some String', 'index': 1}}` \ No newline at end of file diff --git a/guardrails/datatypes.py b/guardrails/datatypes.py index 15913af5f..24d1e099b 100644 --- a/guardrails/datatypes.py +++ b/guardrails/datatypes.py @@ -1,14 +1,16 @@ import datetime import logging +from dataclasses import dataclass from types import SimpleNamespace -from typing import TYPE_CHECKING, Any, Dict, Generator +from typing import TYPE_CHECKING, Any, Dict, Generator, Iterable +from typing import List from typing import List as TypedList from typing import Tuple, Type, Union from lxml import etree as ET from pydantic import BaseModel -from guardrails.utils.logs_utils import FieldValidationLogs, ValidatorLogs +from guardrails.validators import Validator if TYPE_CHECKING: from guardrails.schema import FormatAttr @@ -16,6 +18,30 @@ logger = logging.getLogger(__name__) +@dataclass +class FieldValidation: + key: Any + value: Any + validators: TypedList[Validator] + children: TypedList["FieldValidation"] + + +def verify_metadata_requirements( + metadata: dict, datatypes: Iterable["DataType"] +) -> List[str]: + missing_keys = set() + for datatype in datatypes: + for validator in datatype.validators: + for requirement in validator.required_metadata_keys: + if requirement not in metadata: + missing_keys.add(requirement) + nested_missing_keys = verify_metadata_requirements( + metadata, vars(datatype.children).values() + ) + missing_keys.update(nested_missing_keys) + return list(missing_keys) + + class DataType: def __init__( self, @@ -71,40 +97,30 @@ def from_str(self, s: str) -> "DataType": """ return s - def _iterate_validators( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: - for validator in self.validators: - validator_class_name = validator.__class__.__name__ - validator_logs = ValidatorLogs( - validator_name=validator_class_name, - value_before_validation=value, - ) - validation_logs.validator_logs.append(validator_logs) - logger.debug( - f"Validating field {key} with validator {validator_class_name}..." - ) - schema = validator.validate_with_correction(key, value, schema) - if key in schema: - value = schema[key] - validator_logs.value_after_validation = schema[key] - logger.debug( - f"Validator {validator_class_name} finished, " - f"key {key} has value {schema[key]}." - ) - else: - logger.debug( - f"Validator {validator_class_name} finished, " - f"key {key} is not present in schema." - ) - return schema - - def validate( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: - """Validate a value.""" + def _constructor_validation( + self, + key: str, + value: Any, + ) -> FieldValidation: + """Creates a "FieldValidation" object for ValidatorService to run over, + which specifies the key, value, and validators for a given field. + + Its children should be populated by its nested fields' + FieldValidations. + """ + return FieldValidation( + key=key, value=value, validators=self.validators, children=[] + ) + + def collect_validation( + self, + key: str, + value: Any, + schema: Dict, + ) -> FieldValidation: + """Gather validators on a value.""" value = self.from_str(value) - return self._iterate_validators(validation_logs, key, value, schema) + return self._constructor_validation(key, value) def set_children(self, element: ET._Element): raise NotImplementedError("Abstract method.") @@ -296,25 +312,27 @@ class Percentage(ScalarType): class List(NonScalarType): """Element tag: ``""" - def validate( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: + def collect_validation( + self, + key: str, + value: Any, + schema: Dict, + ) -> FieldValidation: # Validators in the main list data type are applied to the list overall. - self._iterate_validators(validation_logs, key, value, schema) + validation = self._constructor_validation(key, value) if len(self._children) == 0: - return schema + return validation item_type = list(self._children.values())[0] # TODO(shreya): Edge case: List of lists -- does this still work? for i, item in enumerate(value): - child_validation_logs = FieldValidationLogs() - validation_logs.children[i] = child_validation_logs - value = item_type.validate(child_validation_logs, i, item, value) + child_validation = item_type.collect_validation(i, item, value) + validation.children.append(child_validation) - return schema + return validation def set_children(self, element: ET._Element): for idx, child in enumerate(element, start=1): @@ -331,15 +349,18 @@ def set_children(self, element: ET._Element): class Object(NonScalarType): """Element tag: ``""" - def validate( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: + def collect_validation( + self, + key: str, + value: Any, + schema: Dict, + ) -> FieldValidation: # Validators in the main object data type are applied to the object overall. - schema = self._iterate_validators(validation_logs, key, value, schema) + validation = self._constructor_validation(key, value) if len(self._children) == 0: - return schema + return validation # Types of supported children # 1. key_type @@ -354,15 +375,14 @@ def validate( # child_key is an expected key that the schema defined # child_data_type is the data type of the expected key child_value = value.get(child_key, None) - child_validation_logs = FieldValidationLogs() - validation_logs.children[child_key] = child_validation_logs - value = child_data_type.validate( - child_validation_logs, child_key, child_value, value + child_validation = child_data_type.collect_validation( + child_key, + child_value, + value, ) + validation.children.append(child_validation) - schema[key] = value - - return schema + return validation def set_children(self, element: ET._Element): for child in element: @@ -378,23 +398,25 @@ def __init__( self, children: Dict[str, Any], format_attr: "FormatAttr", element: ET._Element ) -> None: super().__init__(children, format_attr, element) + # grab `discriminator` attribute + self.discriminator_key = element.attrib.get("discriminator", "discriminator") - def validate( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: - # Call the validate method of the parent class - super().validate(validation_logs, key, value, schema) - + def collect_validation( + self, + key: str, + value: Any, + schema: Dict, + ) -> FieldValidation: # Validate the selected choice - selected_key = value - selected_value = schema[selected_key] + discriminator_value = value[self.discriminator_key] - self._children[selected_key].validate( - validation_logs, selected_key, selected_value, schema + validation = self._children[discriminator_value].collect_validation( + key, + value, + schema, ) - schema[key] = value - return schema + return validation def set_children(self, element: ET._Element): for child in element: @@ -404,15 +426,7 @@ def set_children(self, element: ET._Element): @property def validators(self) -> TypedList: - from guardrails.validators import Choice as ChoiceValidator - - # Check if the element has an `on-fail` attribute. - # If so, use that as the `on_fail` argument for the PydanticValidator. - on_fail = None - on_fail_attr_name = "on-fail-choice" - if on_fail_attr_name in self.element.attrib: - on_fail = self.element.attrib[on_fail_attr_name] - return [ChoiceValidator(choices=list(self._children.keys()), on_fail=on_fail)] + return [] @register_type("case") @@ -424,20 +438,31 @@ def __init__( ) -> None: super().__init__(children, format_attr, element) - def validate( - self, validation_logs: FieldValidationLogs, key: str, value: Any, schema: Dict - ) -> Dict: - child = list(self._children.values())[0] - - child.validate(validation_logs, key, value, schema) + def collect_validation( + self, + key: str, + value: Any, + schema: Dict, + ) -> FieldValidation: + # Validate the selected choice + validation = self._constructor_validation(key, value) - schema[key] = value + # Collect validation for all children + for child_key, child_data_type in self._children.items(): + # Value should be a dictionary + # child_key is an expected key that the schema defined + # child_data_type is the data type of the expected key + child_value = value.get(child_key, None) + child_validation = child_data_type.collect_validation( + child_key, + child_value, + value, + ) + validation.children.append(child_validation) - return schema + return validation def set_children(self, element: ET._Element): - assert len(element) == 1, "Case must have exactly one child." - for child in element: child_data_type = registry[child.tag] self._children[child.attrib["name"]] = child_data_type.from_xml(child) diff --git a/guardrails/embedding.py b/guardrails/embedding.py index d460c4de9..bcdbe0e88 100644 --- a/guardrails/embedding.py +++ b/guardrails/embedding.py @@ -103,9 +103,13 @@ def __init__( model: Optional[str] = "text-embedding-ada-002", encoding_name: Optional[str] = "cl100k_base", max_tokens: Optional[int] = 8191, + api_key: Optional[str] = None, + api_base: Optional[str] = None, ): super().__init__(model, encoding_name, max_tokens) self._model = model + self.api_key = api_key + self.api_base = api_base def embed(self, texts: List[str]) -> List[List[float]]: embeddings = [] @@ -121,8 +125,14 @@ def embed_query(self, query: str) -> List[float]: return resp[0] def _get_embedding(self, texts: List[str]) -> List[float]: - api_key = os.environ.get("OPENAI_API_KEY") - resp = openai.Embedding.create(api_key=api_key, model=self._model, input=texts) + api_key = ( + self.api_key + if self.api_key is not None + else os.environ.get("OPENAI_API_KEY") + ) + resp = openai.Embedding.create( + api_key=api_key, model=self._model, input=texts, api_base=self.api_base + ) return [r["embedding"] for r in resp["data"]] @property diff --git a/guardrails/guard.py b/guardrails/guard.py index 6b44e556b..2d1e13eef 100644 --- a/guardrails/guard.py +++ b/guardrails/guard.py @@ -1,4 +1,5 @@ import asyncio +import contextvars import logging from string import Formatter from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, Union @@ -13,6 +14,7 @@ from guardrails.schema import Schema from guardrails.utils.logs_utils import GuardState from guardrails.utils.reask_utils import sub_reasks_with_fixed_values +from guardrails.validators import Validator logger = logging.getLogger(__name__) actions_logger = logging.getLogger(f"{__name__}.actions") @@ -23,8 +25,14 @@ class Guard: """The Guard class. This class is the main entry point for using Guardrails. It is - initialized from either `from_rail` or `from_rail_string` methods, - which take in a `.rail` file or string, respectively. The `__call__` + initialized from one of the following class methods: + + - `from_rail` + - `from_rail_string` + - `from_pydantic` + - `from_string` + + The `__call__` method functions as a wrapper around LLM APIs. It takes in an LLM API, and optional prompt parameters, and returns the raw output from the LLM and the validated output. @@ -33,14 +41,15 @@ class Guard: def __init__( self, rail: Rail, - num_reasks: int = 1, + num_reasks: int = None, base_model: Optional[BaseModel] = None, ): """Initialize the Guard.""" self.rail = rail self.num_reasks = num_reasks - self.guard_state = GuardState([]) + self.guard_state = GuardState(all_histories=[]) self._reask_prompt = None + self._reask_instructions = None self.base_model = base_model @property @@ -71,13 +80,10 @@ def raw_prompt(self) -> Prompt: @property def base_prompt(self) -> str: """Return the base prompt i.e. prompt.source.""" + if self.prompt is None: + return None return self.prompt.source - @property - def script(self) -> Optional[Dict]: - """Return the script.""" - return self.rail.script - @property def state(self) -> GuardState: """Return the state.""" @@ -99,18 +105,40 @@ def reask_prompt(self, reask_prompt: Union[str, Prompt]): variables = [ t[1] for t in Formatter().parse(reask_prompt.source) if t[1] is not None ] - assert set(variables) == {"previous_response", "output_schema"} + variable_set = set(variables) + assert variable_set.__contains__("previous_response") + assert variable_set.__contains__("output_schema") self._reask_prompt = reask_prompt + @property + def reask_instructions(self) -> Prompt: + """Return the reask prompt.""" + return self._reask_instructions + + @reask_instructions.setter + def reask_instructions(self, reask_instructions: Union[str, Instructions]): + """Set the reask prompt.""" + + if isinstance(reask_instructions, str): + reask_instructions = Instructions(reask_instructions) + + self._reask_instructions = reask_instructions + def configure( self, - num_reasks: int = 1, + num_reasks: int = None, ): """Configure the Guard.""" - self.num_reasks = num_reasks + self.num_reasks = ( + num_reasks + if num_reasks is not None + else self.num_reasks + if self.num_reasks is not None + else 1 + ) @classmethod - def from_rail(cls, rail_file: str, num_reasks: int = 1) -> "Guard": + def from_rail(cls, rail_file: str, num_reasks: int = None) -> "Guard": """Create a Schema from a `.rail` file. Args: @@ -123,7 +151,7 @@ def from_rail(cls, rail_file: str, num_reasks: int = 1) -> "Guard": return cls(Rail.from_file(rail_file), num_reasks=num_reasks) @classmethod - def from_rail_string(cls, rail_string: str, num_reasks: int = 1) -> "Guard": + def from_rail_string(cls, rail_string: str, num_reasks: int = None) -> "Guard": """Create a Schema from a `.rail` string. Args: @@ -139,9 +167,9 @@ def from_rail_string(cls, rail_string: str, num_reasks: int = 1) -> "Guard": def from_pydantic( cls, output_class: BaseModel, - prompt: str, + prompt: Optional[str] = None, instructions: Optional[str] = None, - num_reasks: int = 1, + num_reasks: int = None, ) -> "Guard": """Create a Guard instance from a Pydantic model and prompt.""" rail = Rail.from_pydantic( @@ -149,6 +177,39 @@ def from_pydantic( ) return cls(rail, num_reasks=num_reasks, base_model=output_class) + @classmethod + def from_string( + cls, + validators: List[Validator], + description: Optional[str] = None, + prompt: Optional[str] = None, + instructions: Optional[str] = None, + reask_prompt: Optional[str] = None, + reask_instructions: Optional[str] = None, + num_reasks: int = None, + ) -> "Guard": + """Create a Guard instance for a string response with prompt, + instructions, and validations. + + Parameters: Arguments + validators: (List[Validator]): The list of validators to apply to the string output. + description (str, optional): A description for the string to be generated. Defaults to None. + prompt (str, optional): The prompt used to generate the string. Defaults to None. + instructions (str, optional): Instructions for chat models. Defaults to None. + reask_prompt (str, optional): An alternative prompt to use during reasks. Defaults to None. + reask_instructions (str, optional): Alternative instructions to use during reasks. Defaults to None. + num_reasks (int, optional): The max times to re-ask the LLM for invalid output. + """ # noqa + rail = Rail.from_string_validators( + validators=validators, + description=description, + prompt=prompt, + instructions=instructions, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + ) + return cls(rail, num_reasks=num_reasks) + def __call__( self, llm_api: Union[Callable, Callable[[Any], Awaitable[Any]]], @@ -156,7 +217,9 @@ def __call__( num_reasks: Optional[int] = None, prompt: Optional[str] = None, instructions: Optional[str] = None, - chat_history: Optional[List[Dict]] = None, + msg_history: Optional[List[Dict]] = None, + metadata: Optional[Dict] = None, + full_schema_reask: Optional[bool] = None, *args, **kwargs, ) -> Union[Tuple[str, Dict], Awaitable[Tuple[str, Dict]]]: @@ -168,22 +231,38 @@ def __call__( (e.g. openai.Completion.create or openai.Completion.acreate) prompt_params: The parameters to pass to the prompt.format() method. num_reasks: The max times to re-ask the LLM for invalid output. + prompt: The prompt to use for the LLM. + instructions: Instructions for chat models. + msg_history: The message history to pass to the LLM. + metadata: Metadata to pass to the validators. + full_schema_reask: When reasking, whether to regenerate the full schema + or just the incorrect values. + Defaults to `True` if a base model is provided, + `False` otherwise. Returns: The raw text output from the LLM and the validated output. """ - if num_reasks is None: - num_reasks = self.num_reasks + self.configure(num_reasks) + if metadata is None: + metadata = {} + if full_schema_reask is None: + full_schema_reask = self.base_model is not None + + context = contextvars.ContextVar("kwargs") + context.set(kwargs) # If the LLM API is async, return a coroutine if asyncio.iscoroutinefunction(llm_api): return self._call_async( llm_api, prompt_params=prompt_params, - num_reasks=num_reasks, + num_reasks=self.num_reasks, prompt=prompt, instructions=instructions, - chat_history=chat_history, + msg_history=msg_history, + metadata=metadata, + full_schema_reask=full_schema_reask, *args, **kwargs, ) @@ -191,10 +270,12 @@ def __call__( return self._call_sync( llm_api, prompt_params=prompt_params, - num_reasks=num_reasks, + num_reasks=self.num_reasks, prompt=prompt, instructions=instructions, - chat_history=chat_history, + msg_history=msg_history, + metadata=metadata, + full_schema_reask=full_schema_reask, *args, **kwargs, ) @@ -202,21 +283,23 @@ def __call__( def _call_sync( self, llm_api: Callable, - prompt_params: Dict = None, - num_reasks: int = 1, - prompt: Optional[str] = None, - instructions: Optional[str] = None, - chat_history: Optional[List[Dict]] = None, + prompt_params: Dict, + num_reasks: int, + prompt: Optional[str], + instructions: Optional[str], + msg_history: Optional[List[Dict]], + metadata: Dict, + full_schema_reask: bool, *args, **kwargs, ) -> Tuple[str, Dict]: instructions = instructions or self.instructions prompt = prompt or self.prompt - chat_history = chat_history or [] + msg_history = msg_history or [] if prompt is None: - if not len(chat_history): + if not len(msg_history): raise RuntimeError( - "You must provide a prompt if chat_history is empty. " + "You must provide a prompt if msg_history is empty. " "Alternatively, you can provide a prompt in the Schema constructor." ) @@ -224,13 +307,17 @@ def _call_sync( runner = Runner( instructions=instructions, prompt=prompt, + msg_history=msg_history, api=get_llm_ask(llm_api, *args, **kwargs), input_schema=self.input_schema, output_schema=self.output_schema, num_reasks=num_reasks, + metadata=metadata, reask_prompt=self.reask_prompt, + reask_instructions=self.reask_instructions, base_model=self.base_model, guard_state=self.guard_state, + full_schema_reask=full_schema_reask, ) guard_history = runner(prompt_params=prompt_params) return guard_history.output, guard_history.validated_output @@ -238,11 +325,13 @@ def _call_sync( async def _call_async( self, llm_api: Callable[[Any], Awaitable[Any]], - prompt_params: Dict = None, - num_reasks: int = 1, - prompt: Optional[str] = None, - instructions: Optional[str] = None, - chat_history: Optional[List[Dict]] = None, + prompt_params: Dict, + num_reasks: int, + prompt: Optional[str], + instructions: Optional[str], + msg_history: Optional[List[Dict]], + metadata: Dict, + full_schema_reask: bool, *args, **kwargs, ) -> Tuple[str, Dict]: @@ -252,30 +341,42 @@ async def _call_async( llm_api: The LLM API to call asynchronously (e.g. openai.Completion.acreate) prompt_params: The parameters to pass to the prompt.format() method. num_reasks: The max times to re-ask the LLM for invalid output. + prompt: The prompt to use for the LLM. + instructions: Instructions for chat models. + msg_history: The message history to pass to the LLM. + metadata: Metadata to pass to the validators. + full_schema_reask: When reasking, whether to regenerate the full schema + or just the incorrect values. + Defaults to `True` if a base model is provided, + `False` otherwise. Returns: The raw text output from the LLM and the validated output. """ instructions = instructions or self.instructions prompt = prompt or self.prompt - chat_history = chat_history or [] + msg_history = msg_history or [] if prompt is None: - if not len(chat_history): + if not len(msg_history): raise RuntimeError( - "You must provide a prompt if chat_history is empty. " - "Alternatively, you can provide a prompt in the Schema constructor." + "You must provide a prompt if msg_history is empty. " + "Alternatively, you can provide a prompt in the RAIL spec." ) with start_action(action_type="guard_call", prompt_params=prompt_params): runner = AsyncRunner( instructions=instructions, prompt=prompt, + msg_history=msg_history, api=get_async_llm_ask(llm_api, *args, **kwargs), input_schema=self.input_schema, output_schema=self.output_schema, num_reasks=num_reasks, + metadata=metadata, reask_prompt=self.reask_prompt, + reask_instructions=self.reask_instructions, base_model=self.base_model, guard_state=self.guard_state, + full_schema_reask=full_schema_reask, ) guard_history = await runner.async_run(prompt_params=prompt_params) return guard_history.output, guard_history.validated_output @@ -289,9 +390,11 @@ def __rich_repr__(self): def parse( self, llm_output: str, + metadata: Optional[Dict] = None, llm_api: Union[Callable, Callable[[Any], Awaitable[Any]]] = None, - num_reasks: int = 1, + num_reasks: int = None, prompt_params: Dict = None, + full_schema_reask: bool = None, *args, **kwargs, ) -> Union[Tuple[str, Dict], Awaitable[Tuple[str, Dict]]]: @@ -305,22 +408,37 @@ def parse( Returns: The validated response. """ + num_reasks = ( + num_reasks if num_reasks is not None else 0 if llm_api is None else None + ) + self.configure(num_reasks) + if full_schema_reask is None: + full_schema_reask = self.base_model is not None + metadata = metadata or {} + + context = contextvars.ContextVar("kwargs") + context.set(kwargs) + # If the LLM API is async, return a coroutine if asyncio.iscoroutinefunction(llm_api): return self._async_parse( llm_output, + metadata, llm_api=llm_api, - num_reasks=num_reasks, + num_reasks=self.num_reasks, prompt_params=prompt_params, + full_schema_reask=full_schema_reask, *args, **kwargs, ) # Otherwise, call the LLM synchronously return self._sync_parse( llm_output, + metadata, llm_api=llm_api, - num_reasks=num_reasks, + num_reasks=self.num_reasks, prompt_params=prompt_params, + full_schema_reask=full_schema_reask, *args, **kwargs, ) @@ -328,9 +446,11 @@ def parse( def _sync_parse( self, llm_output: str, + metadata: Dict, llm_api: Callable = None, num_reasks: int = 1, prompt_params: Dict = None, + full_schema_reask: bool = False, *args, **kwargs, ) -> Dict: @@ -346,16 +466,20 @@ def _sync_parse( """ with start_action(action_type="guard_parse"): runner = Runner( - instructions=None, - prompt=None, + instructions=kwargs.get("instructions", None), + prompt=kwargs.get("prompt", None), + msg_history=kwargs.get("msg_history", None), api=get_llm_ask(llm_api, *args, **kwargs) if llm_api else None, input_schema=None, output_schema=self.output_schema, num_reasks=num_reasks, + metadata=metadata, output=llm_output, reask_prompt=self.reask_prompt, + reask_instructions=self.reask_instructions, base_model=self.base_model, guard_state=self.guard_state, + full_schema_reask=full_schema_reask, ) guard_history = runner(prompt_params=prompt_params) return sub_reasks_with_fixed_values(guard_history.validated_output) @@ -363,9 +487,11 @@ def _sync_parse( async def _async_parse( self, llm_output: str, + metadata: Dict, llm_api: Callable[[Any], Awaitable[Any]] = None, num_reasks: int = 1, prompt_params: Dict = None, + full_schema_reask: bool = False, *args, **kwargs, ) -> Dict: @@ -383,14 +509,18 @@ async def _async_parse( runner = AsyncRunner( instructions=None, prompt=None, + msg_history=None, api=get_async_llm_ask(llm_api, *args, **kwargs) if llm_api else None, input_schema=None, output_schema=self.output_schema, num_reasks=num_reasks, + metadata=metadata, output=llm_output, reask_prompt=self.reask_prompt, + reask_instructions=self.reask_instructions, base_model=self.base_model, guard_state=self.guard_state, + full_schema_reask=full_schema_reask, ) guard_history = await runner.async_run(prompt_params=prompt_params) return sub_reasks_with_fixed_values(guard_history.validated_output) diff --git a/guardrails/llm_providers.py b/guardrails/llm_providers.py index 574fbeb92..2f49e00ff 100644 --- a/guardrails/llm_providers.py +++ b/guardrails/llm_providers.py @@ -7,12 +7,20 @@ from pydantic import BaseModel from tenacity import retry, retry_if_exception_type, wait_exponential_jitter +from guardrails.utils.pydantic_utils import convert_pydantic_model_to_openai_fn + try: MANIFEST = True import manifest except ImportError: MANIFEST = False +try: + import cohere +except ImportError: + cohere = None + + OPENAI_RETRYABLE_ERRORS = [ openai.error.APIConnectionError, openai.error.APIError, @@ -69,20 +77,25 @@ def __call__(self, *args, **kwargs): return result -def nonchat_prompt(prompt: str, instructions: Optional[str] = None, **kwargs) -> str: +def nonchat_prompt(prompt: str, instructions: Optional[str] = None) -> str: """Prepare final prompt for nonchat engine.""" if instructions: prompt = "\n\n".join([instructions, prompt]) - return prompt def chat_prompt( - prompt: str, instructions: Optional[str] = None, **kwargs + prompt: str, + instructions: Optional[str] = None, + msg_history: Optional[List[Dict]] = None, ) -> List[Dict[str, str]]: """Prepare final prompt for chat engine.""" + if msg_history: + return msg_history + if not instructions: instructions = "You are a helpful assistant." + return [ {"role": "system", "content": instructions}, {"role": "user", "content": prompt}, @@ -95,12 +108,12 @@ def openai_wrapper( instructions: Optional[str] = None, *args, **kwargs, -): - api_key = os.environ.get("OPENAI_API_KEY") +) -> str: + api_key = kwargs.pop("api_key", os.environ.get("OPENAI_API_KEY")) openai_response = openai.Completion.create( api_key=api_key, engine=engine, - prompt=nonchat_prompt(text, instructions, **kwargs), + prompt=nonchat_prompt(prompt=text, instructions=instructions), *args, **kwargs, ) @@ -108,51 +121,71 @@ def openai_wrapper( def openai_chat_wrapper( - text: str, - model="gpt-3.5-turbo", + text: Optional[str] = None, + model: str = "gpt-3.5-turbo", instructions: Optional[str] = None, + msg_history: Optional[List[Dict]] = None, base_model: Optional[BaseModel] = None, + function_call: Optional[str] = None, *args, **kwargs, -): - if base_model: - base_model_schema = base_model.schema() - function_params = { - "name": base_model_schema["title"], - "description": base_model_schema["description"] - if "description" in base_model_schema - else None, - "parameters": base_model_schema, - } - - api_key = os.environ.get("OPENAI_API_KEY") - - # TODO: update this as new models are released - if base_model: - openai_response = openai.ChatCompletion.create( - api_key=api_key, - model=model, - messages=chat_prompt(text, instructions, **kwargs), - functions=[function_params], - function_call={"name": function_params["name"]}, - *args, - **kwargs, +) -> str: + """Wrapper for OpenAI chat engines. + + Use Guardrails with OpenAI chat engines by doing + ``` + raw_llm_response, validated_response = guard( + openai.ChatCompletion.create, + prompt_params={...}, + text=..., + instructions=..., + msg_history=..., + temperature=..., + ... + ) + ``` + + If `base_model` is passed, the chat engine will be used as a function + on the base model. + """ + + if msg_history is None and text is None: + raise PromptCallableException( + "You must pass in either `text` or `msg_history` to `guard.__call__`." ) + + # Configure function calling if applicable + if base_model: + function_params = [convert_pydantic_model_to_openai_fn(base_model)] + if function_call is None: + function_call = {"name": function_params[0]["name"]} + fn_kwargs = {"functions": function_params, "function_call": function_call} + else: + fn_kwargs = {} + + # Call OpenAI + api_key = kwargs.pop("api_key", os.environ.get("OPENAI_API_KEY")) + openai_response = openai.ChatCompletion.create( + api_key=api_key, + model=model, + messages=chat_prompt( + prompt=text, instructions=instructions, msg_history=msg_history + ), + *args, + **fn_kwargs, + **kwargs, + ) + + # Extract string from response + if "function_call" in openai_response["choices"][0]["message"]: return openai_response["choices"][0]["message"]["function_call"]["arguments"] else: - openai_response = openai.ChatCompletion.create( - api_key=api_key, - model=model, - messages=chat_prompt(text, instructions, **kwargs), - *args, - **kwargs, - ) return openai_response["choices"][0]["message"]["content"] def manifest_wrapper( text: str, client: Any, instructions: Optional[str] = None, *args, **kwargs -): +) -> str: """Wrapper for manifest client. To use manifest for guardrailse, do @@ -171,11 +204,36 @@ def manifest_wrapper( ) client = cast(manifest.Manifest, client) manifest_response = client.run( - nonchat_prompt(text, instructions, **kwargs), *args, **kwargs + nonchat_prompt(prompt=text, instructions=instructions), *args, **kwargs ) return manifest_response +def cohere_wrapper( + prompt: str, client_callable: Any, model: str, *args, **kwargs +) -> str: + """Wrapper for cohere client. + + To use cohere for guardrails, do + ``` + client = cohere.Client(api_key=...) + + raw_llm_response, validated_response = guard( + client.generate, + prompt_params={...}, + model="command-nightly", + ... + ) + ``` + """ + + if "instructions" in kwargs: + prompt = kwargs.pop("instructions") + "\n\n" + prompt + + cohere_response = client_callable(prompt=prompt, model=model, *args, **kwargs) + return cohere_response[0].text + + def get_llm_ask(llm_api: Callable, *args, **kwargs) -> PromptCallable: if llm_api == openai.Completion.create: fn = partial(openai_wrapper, *args, **kwargs) @@ -183,6 +241,12 @@ def get_llm_ask(llm_api: Callable, *args, **kwargs) -> PromptCallable: fn = partial(openai_chat_wrapper, *args, **kwargs) elif MANIFEST and isinstance(llm_api, manifest.Manifest): fn = partial(manifest_wrapper, client=llm_api, *args, **kwargs) + elif ( + cohere + and isinstance(getattr(llm_api, "__self__", None), cohere.Client) + and getattr(llm_api, "__name__", None) == "generate" + ): + fn = partial(cohere_wrapper, client_callable=llm_api, *args, **kwargs) else: # Let the user pass in an arbitrary callable. fn = partial(llm_api, *args, **kwargs) @@ -238,11 +302,11 @@ async def async_openai_wrapper( *args, **kwargs, ): - api_key = os.environ.get("OPENAI_API_KEY") + api_key = kwargs.pop("api_key", os.environ.get("OPENAI_API_KEY")) openai_response = await openai.Completion.acreate( api_key=api_key, engine=engine, - prompt=nonchat_prompt(text, instructions, **kwargs), + prompt=nonchat_prompt(prompt=text, instructions=instructions), *args, **kwargs, ) @@ -256,11 +320,11 @@ async def async_openai_chat_wrapper( *args, **kwargs, ): - api_key = os.environ.get("OPENAI_API_KEY") + api_key = kwargs.pop("api_key", os.environ.get("OPENAI_API_KEY")) openai_response = await openai.ChatCompletion.acreate( api_key=api_key, model=model, - messages=chat_prompt(text, instructions, **kwargs), + messages=chat_prompt(prompt=text, instructions=instructions), *args, **kwargs, ) @@ -288,7 +352,7 @@ async def async_manifest_wrapper( ) client = cast(manifest.Manifest, client) manifest_response = await client.run( - nonchat_prompt(text, instructions, **kwargs), *args, **kwargs + nonchat_prompt(prompt=text, instructions=instructions), *args, **kwargs ) return manifest_response diff --git a/guardrails/namespace_template.py b/guardrails/namespace_template.py new file mode 100644 index 000000000..c9dcfc1c9 --- /dev/null +++ b/guardrails/namespace_template.py @@ -0,0 +1,6 @@ +import string + + +class NamespaceTemplate(string.Template): + delimiter = "$" + idpattern = r"[a-z][_a-z0-9.]*" diff --git a/guardrails/prompt/base_prompt.py b/guardrails/prompt/base_prompt.py index 303bbb110..4c3ae336c 100644 --- a/guardrails/prompt/base_prompt.py +++ b/guardrails/prompt/base_prompt.py @@ -1,8 +1,11 @@ """Class for representing a prompt entry.""" import re -from string import Formatter +from string import Formatter, Template from typing import Optional +import regex + +from guardrails.namespace_template import NamespaceTemplate from guardrails.utils.constants import constants @@ -17,7 +20,7 @@ def __init__(self, source: str, output_schema: Optional[str] = None): # If an output schema is provided, substitute it in the prompt. if output_schema: - self.source = source.format(output_schema=output_schema) + self.source = Template(source).safe_substitute(output_schema=output_schema) else: self.source = source @@ -33,7 +36,7 @@ def __str__(self) -> str: @property def variable_names(self): - return [x[1] for x in Formatter().parse(self.source) if x[1] is not None] + return [x[1] for x in Formatter().parse(self.escape()) if x[1] is not None] @property def format_instructions(self): @@ -42,13 +45,16 @@ def format_instructions(self): def substitute_constants(self, text): """Substitute constants in the prompt.""" # Substitute constants by reading the constants file. - # Regex to extract all occurrences of @ - matches = re.findall(r"@(\w+)", text) + # Regex to extract all occurrences of ${gr.} + + matches = re.findall(r"\${gr.(\w+)}", text) - # Substitute all occurrences of @ with the value of the constant. + # Substitute all occurrences of ${gr.} + # with the value of the constant. for match in matches: - if match in constants: - text = text.replace(f"@{match}", constants[match]) + template = NamespaceTemplate(text) + mapping = {f"gr.{match}": constants[match]} + text = template.safe_substitute(**mapping) return text @@ -74,9 +80,9 @@ def get_format_instructions_idx(self, text: str) -> Optional[int]: """ # TODO(shreya): Optionally add support for special character demarcation. - # Regex to extract first occurrence of @ + # Regex to extract first occurrence of ${gr.} - matches = re.finditer(r"@(\w+)", text) + matches = re.finditer(r"\${gr.(\w+)}", text) earliest_match_idx = None earliest_match = None @@ -92,3 +98,8 @@ def get_format_instructions_idx(self, text: str) -> Optional[int]: return 0 return earliest_match.start() + + def escape(self) -> str: + start_replaced = regex.sub(r"(? "Script": - if "language" not in root.attrib: - raise ValueError("Script element must have a language attribute.") - - language = root.attrib["language"] - if language != "python": - raise ValueError("Only python scripts are supported right now.") - - # Run the script in the global namespace, returning the additional - # globals that were created. - keys = set(globals().keys()) - exec(root.text, globals()) - new_keys = globals().keys() - variables = {k: globals()[k] for k in new_keys if k not in keys} - return cls(variables, language) - - @staticmethod - def find_expressions(body) -> List[str]: - """Get all expressions, written as {...} in a string body.""" - expressions = [] - stack = [] - start = -1 - - for i, char in enumerate(body): - if char == "{": - if not stack: - start = i - stack.append(char) - elif char == "}": - if stack and stack[-1] == "{": - stack.pop() - if not stack: - expressions.append(body[start + 1 : i]) - else: - stack.append(char) - return expressions - - def replace_expressions(self, body: str) -> str: - """Replace all expressions in a string body with their evaluated - values.""" - # Decode the body if it's a bytes object. - if isinstance(body, bytes): - body = body.decode("utf-8") - for expr in self.find_expressions(body): - # The replacement should be inserted as a Python expression, inside - # curly braces. - replacement = self(expr) - # If a string, wrap it in '' quotes. - if isinstance(replacement, str): - replacement = f"'{replacement}'" - # Escape any double quotes. - replacement = str(replacement).replace('"', """) - # Replace the expression with the evaluated value. - body = body.replace(f"{{{expr}}}", f"{{{replacement}}}") - - return body - - def __call__(self, expr: str): - """Eval expression in the script's namespace.""" - return eval(expr, {**globals(), **self.variables}) - - @dataclass class Rail: """RAIL (Reliable AI Language) is a dialect of XML that allows users to @@ -91,24 +27,34 @@ class Rail: A RAIL file contains a root element called `` that contains the following elements as children: - 1. ` - @@ -51,10 +11,10 @@ class Person(BaseModel): Generate data for possible users in accordance with the specification below. -@xml_prefix_prompt +${gr.xml_prefix_prompt} -{output_schema} +${output_schema} -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2} diff --git a/tests/integration_tests/test_assets/pydantic/validated_response_reask.py b/tests/integration_tests/test_assets/pydantic/validated_response_reask.py new file mode 100644 index 000000000..83e25307c --- /dev/null +++ b/tests/integration_tests/test_assets/pydantic/validated_response_reask.py @@ -0,0 +1,136 @@ +# flake8: noqa: E501 +from typing import Any, Dict, List + +from pydantic import BaseModel, Field + +from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import ( + FailResult, + PassResult, + ValidationResult, + Validator, + register_validator, +) + +prompt = """Generate data for possible users in accordance with the specification below. + +${gr.xml_prefix_prompt} + +${output_schema} + +${gr.complete_json_suffix_v2}""" + + +@register_validator(name="zip_code_must_be_numeric", data_type="string") +class ZipCodeMustBeNumeric(Validator): + def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult: + if not value.isnumeric(): + return FailResult(error_message="Zip code must be numeric.") + return PassResult() + + +@register_validator(name="age_must_be_between_0_and_150", data_type="integer") +class AgeMustBeBetween0And150(Validator): + def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult: + if not 0 <= value <= 150: + return FailResult(error_message="Age must be between 0 and 150.") + return PassResult() + + +@register_validator(name="zip_code_in_california", data_type="string") +class ZipCodeInCalifornia(Validator): + def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult: + if not value.startswith("9"): + return FailResult( + error_message="Zip code must be in California, and start with 9." + ) + if value == "90210": + return FailResult(error_message="Zip code must not be Beverly Hills.") + return PassResult() + + +class Person(BaseModel): + """Information about a person. + + Args: + name (str): The name of the person. + age (int): The age of the person. + zip_code (str): The zip code of the person. + """ + + name: str + age: int = Field(..., validators=[AgeMustBeBetween0And150(on_fail="reask")]) + zip_code: str = Field( + ..., + validators=[ + ZipCodeMustBeNumeric(on_fail="reask"), + ZipCodeInCalifornia(on_fail="reask"), + ], + ) + + +class ListOfPeople(BaseModel): + """A list of people. + + Args: + people (List[Person]): A list of people. + """ + + people: List[Person] + + +VALIDATED_OUTPUT_1 = { + "people": [ + { + "name": "John Doe", + "age": 28, + "zip_code": FieldReAsk( + incorrect_value="90210", + fail_results=[ + FailResult( + error_message="Zip code must not be Beverly Hills.", + fix_value=None, + ) + ], + path=["people", 0, "zip_code"], + ), + }, + {"name": "Jane Doe", "age": 32, "zip_code": "94103"}, + {"name": "James Smith", "age": 40, "zip_code": "92101"}, + ] +} + + +VALIDATED_OUTPUT_2 = { + "people": [ + { + "name": "John Doe", + "age": 28, + "zip_code": FieldReAsk( + incorrect_value="None", + fail_results=[ + FailResult( + error_message="Zip code must be numeric.", + fix_value=None, + ), + FailResult( + error_message="Zip code must be in California, and start with 9.", + fix_value=None, + ), + ], + path=["people", 0, "zip_code"], + ), + }, + {"name": "Jane Doe", "age": 32, "zip_code": "94103"}, + {"name": "James Smith", "age": 40, "zip_code": "92101"}, + ] +} + + +VALIDATED_OUTPUT_3 = { + "people": [ + {"name": "John Doe", "age": 28, "zip_code": None}, + {"name": "Jane Doe", "age": 32, "zip_code": "94103"}, + {"name": "James Smith", "age": 40, "zip_code": "92101"}, + ] +} diff --git a/tests/integration_tests/test_assets/pydantic/validated_response_reask_1.py b/tests/integration_tests/test_assets/pydantic/validated_response_reask_1.py deleted file mode 100644 index 8ef4fb6c1..000000000 --- a/tests/integration_tests/test_assets/pydantic/validated_response_reask_1.py +++ /dev/null @@ -1,58 +0,0 @@ -# flake8: noqa: E501 -from pydantic import BaseModel, validator - -from guardrails.utils.pydantic_utils import register_pydantic -from guardrails.utils.reask_utils import FieldReAsk - - -@register_pydantic -class Person(BaseModel): - """Information about a person. - - Args: - name (str): The name of the person. - age (int): The age of the person. - zip_code (str): The zip code of the person. - """ - - name: str - age: int - zip_code: str - - @validator("zip_code") - def zip_code_must_be_numeric(cls, v): - if not v.isnumeric(): - raise ValueError("Zip code must be numeric.") - return v - - @validator("age") - def age_must_be_between_0_and_150(cls, v): - if not 0 <= v <= 150: - raise ValueError("Age must be between 0 and 150.") - return v - - @validator("zip_code") - def zip_code_in_california(cls, v): - if not v.startswith("9"): - raise ValueError("Zip code must be in California, and start with 9.") - if v == "90210": - raise ValueError("Zip code must not be Beverly Hills.") - return v - - -VALIDATED_OUTPUT = { - "people": [ - { - "name": "John Doe", - "age": 28, - "zip_code": FieldReAsk( - incorrect_value="90210", - error_message="Zip code must not be Beverly Hills.", - fix_value=None, - path=["people", 0], - ), - }, - Person(name="Jane Doe", age=32, zip_code="94103"), - Person(name="James Smith", age=40, zip_code="92101"), - ] -} diff --git a/tests/integration_tests/test_assets/pydantic/validated_response_reask_2.py b/tests/integration_tests/test_assets/pydantic/validated_response_reask_2.py deleted file mode 100644 index 8c8b1c44f..000000000 --- a/tests/integration_tests/test_assets/pydantic/validated_response_reask_2.py +++ /dev/null @@ -1,58 +0,0 @@ -# flake8: noqa: E501 -from pydantic import BaseModel, validator - -from guardrails.utils.pydantic_utils import register_pydantic -from guardrails.utils.reask_utils import FieldReAsk - - -@register_pydantic -class Person(BaseModel): - """Information about a person. - - Args: - name (str): The name of the person. - age (int): The age of the person. - zip_code (str): The zip code of the person. - """ - - name: str - age: int - zip_code: str - - @validator("zip_code") - def zip_code_must_be_numeric(cls, v): - if not v.isnumeric(): - raise ValueError("Zip code must be numeric.") - return v - - @validator("age") - def age_must_be_between_0_and_150(cls, v): - if not 0 <= v <= 150: - raise ValueError("Age must be between 0 and 150.") - return v - - @validator("zip_code") - def zip_code_in_california(cls, v): - if not v.startswith("9"): - raise ValueError("Zip code must be in California, and start with 9.") - if v == "90210": - raise ValueError("Zip code must not be Beverly Hills.") - return v - - -VALIDATED_OUTPUT = { - "people": [ - { - "name": "John Doe", - "age": 28, - "zip_code": FieldReAsk( - incorrect_value="None", - error_message="Zip code must be numeric.", - fix_value=None, - path=["people", 0], - ), - }, - Person(name="Jane Doe", age=32, zip_code="94103"), - Person(name="James Smith", age=40, zip_code="92101"), - ] -} diff --git a/tests/integration_tests/test_assets/pydantic/validated_response_reask_3.py b/tests/integration_tests/test_assets/pydantic/validated_response_reask_3.py deleted file mode 100644 index 4b103817f..000000000 --- a/tests/integration_tests/test_assets/pydantic/validated_response_reask_3.py +++ /dev/null @@ -1,49 +0,0 @@ -# flake8: noqa: E501 - -from pydantic import BaseModel, validator - -from guardrails.utils.pydantic_utils import register_pydantic - - -@register_pydantic -class Person(BaseModel): - """Information about a person. - - Args: - name (str): The name of the person. - age (int): The age of the person. - zip_code (str): The zip code of the person. - """ - - name: str - age: int - zip_code: str - - @validator("zip_code") - def zip_code_must_be_numeric(cls, v): - if not v.isnumeric(): - raise ValueError("Zip code must be numeric.") - return v - - @validator("age") - def age_must_be_between_0_and_150(cls, v): - if not 0 <= v <= 150: - raise ValueError("Age must be between 0 and 150.") - return v - - @validator("zip_code") - def zip_code_in_california(cls, v): - if not v.startswith("9"): - raise ValueError("Zip code must be in California, and start with 9.") - if v == "90210": - raise ValueError("Zip code must not be Beverly Hills.") - return v - - -VALIDATED_OUTPUT = { - "people": [ - {"name": "John Doe", "age": 28, "zip_code": None}, - Person(name="Jane Doe", age=32, zip_code="94103"), - Person(name="James Smith", age=40, zip_code="92101"), - ] -} diff --git a/tests/integration_tests/test_assets/pydantic/with_msg_history.py b/tests/integration_tests/test_assets/pydantic/with_msg_history.py new file mode 100644 index 000000000..0df7b6edb --- /dev/null +++ b/tests/integration_tests/test_assets/pydantic/with_msg_history.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel, Field + + +class Movie(BaseModel): + # """Details about a movie.""" + name: str = Field(..., description="The name of the movie.") + director: str = Field(..., description="The name of the director.") + release_year: int = Field(..., description="The year the movie was released.") diff --git a/tests/integration_tests/test_assets/python_rail/__init__.py b/tests/integration_tests/test_assets/python_rail/__init__.py index d87532994..f845e1895 100644 --- a/tests/integration_tests/test_assets/python_rail/__init__.py +++ b/tests/integration_tests/test_assets/python_rail/__init__.py @@ -20,6 +20,18 @@ "llm_output_3_succeed_gd_and_pydantic.txt" ) +RAIL_SPEC_WITH_VALIDATOR_PARALLELISM = reader("validator_parallelism.rail") +VALIDATOR_PARALLELISM_PROMPT_1 = reader("validator_parallelism_prompt_1.txt") +VALIDATOR_PARALLELISM_RESPONSE_1 = reader("validator_parallelism_1.txt") +from .validator_parallelism_reask_1 import VALIDATOR_PARALLELISM_REASK_1 + +VALIDATOR_PARALLELISM_PROMPT_2 = reader("validator_parallelism_prompt_2.txt") +VALIDATOR_PARALLELISM_RESPONSE_2 = reader("validator_parallelism_2.txt") +from .validator_parallelism_reask_2 import VALIDATOR_PARALLELISM_REASK_2 + +VALIDATOR_PARALLELISM_PROMPT_3 = reader("validator_parallelism_prompt_3.txt") +VALIDATOR_PARALLELISM_RESPONSE_3 = reader("validator_parallelism_3.txt") + __all__ = [ "COMPILED_PROMPT_1_WITHOUT_INSTRUCTIONS", "COMPILED_PROMPT_2_WITHOUT_INSTRUCTIONS", diff --git a/tests/integration_tests/test_assets/python_rail/compiled_prompt_1.txt b/tests/integration_tests/test_assets/python_rail/compiled_prompt_1.txt index cde05a6d1..83d7de3a5 100644 --- a/tests/integration_tests/test_assets/python_rail/compiled_prompt_1.txt +++ b/tests/integration_tests/test_assets/python_rail/compiled_prompt_1.txt @@ -15,15 +15,16 @@ Given below is XML that describes the information to extract from this document - - - - - - - - - + + + + + + + + + +
diff --git a/tests/integration_tests/test_assets/python_rail/compiled_prompt_2.txt b/tests/integration_tests/test_assets/python_rail/compiled_prompt_2.txt index 8908c2e2d..ca7806694 100644 --- a/tests/integration_tests/test_assets/python_rail/compiled_prompt_2.txt +++ b/tests/integration_tests/test_assets/python_rail/compiled_prompt_2.txt @@ -7,7 +7,9 @@ I was given the following JSON response, which had problems due to incorrect val "details": { "website": { "incorrect_value": "a.b.c", - "error_message": "Value has length less than 9. Please return a longer output, that is shorter than 100 characters." + "error_messages": [ + "Value has length less than 9. Please return a longer output, that is shorter than 100 characters." + ] } } } diff --git a/tests/integration_tests/test_assets/python_rail/llm_output_1_fail_guardrails_validation.txt b/tests/integration_tests/test_assets/python_rail/llm_output_1_fail_guardrails_validation.txt index a04eb4ee3..43c2cf74e 100644 --- a/tests/integration_tests/test_assets/python_rail/llm_output_1_fail_guardrails_validation.txt +++ b/tests/integration_tests/test_assets/python_rail/llm_output_1_fail_guardrails_validation.txt @@ -11,8 +11,8 @@ "is_sequel": false, "website": "a.b.c", "contact_email": "info@inceptionmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 829895144.0, "opening_weekend": 62785337.0 } @@ -28,8 +28,8 @@ "is_sequel": true, "website": "https://www.thedarkknightmovie.com", "contact_email": "info@thedarkknightmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 1004558444.0, "opening_weekend": 158411483.0 } @@ -45,8 +45,8 @@ "is_sequel": true, "website": "https://www.thedarkknightrises.com", "contact_email": "info@thedarkknightrises.com", - "revenue_type": "streaming", - "streaming": { + "revenue": { + "revenue_type": "streaming", "subscriptions": 15000000, "subscription_fee": 9.99 } @@ -62,8 +62,8 @@ "is_sequel": false, "website": "https://www.interstellarmovie.com", "contact_email": "info@interstellarmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 115000000.0, "opening_weekend": 47510360.0 } @@ -79,8 +79,8 @@ "is_sequel": false, "website": "https://www.dunkirkmovie.com", "contact_email": "info@dunkirkmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 526940665.0, "opening_weekend": 50513488.0 } diff --git a/tests/integration_tests/test_assets/python_rail/llm_output_2_succeed_gd_but_fail_pydantic_validation.txt b/tests/integration_tests/test_assets/python_rail/llm_output_2_succeed_gd_but_fail_pydantic_validation.txt index 2bb8db360..a8298896f 100644 --- a/tests/integration_tests/test_assets/python_rail/llm_output_2_succeed_gd_but_fail_pydantic_validation.txt +++ b/tests/integration_tests/test_assets/python_rail/llm_output_2_succeed_gd_but_fail_pydantic_validation.txt @@ -11,8 +11,8 @@ "is_sequel": false, "website": "https://www.inceptionmovie.com", "contact_email": "info@inceptionmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 829895144.0, "opening_weekend": 62785337.0 } @@ -28,8 +28,8 @@ "is_sequel": true, "website": "https://www.thedarkknightmovie.com", "contact_email": "info@thedarkknightmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 1004558444.0, "opening_weekend": 158411483.0 } @@ -45,8 +45,8 @@ "is_sequel": true, "website": "https://www.thedarkknightrises.com", "contact_email": "info@thedarkknightrises.com", - "revenue_type": "streaming", - "streaming": { + "revenue": { + "revenue_type": "streaming", "subscriptions": 15000000, "subscription_fee": 9.99 } @@ -62,8 +62,8 @@ "is_sequel": false, "website": "https://www.interstellarmovie.com", "contact_email": "info@interstellarmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 115000000.0, "opening_weekend": 47510360.0 } @@ -79,8 +79,8 @@ "is_sequel": false, "website": "https://www.dunkirkmovie.com", "contact_email": "info@dunkirkmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 526940665.0, "opening_weekend": 50513488.0 } diff --git a/tests/integration_tests/test_assets/python_rail/llm_output_3_succeed_gd_and_pydantic.txt b/tests/integration_tests/test_assets/python_rail/llm_output_3_succeed_gd_and_pydantic.txt index 83c830cdf..5c9363849 100644 --- a/tests/integration_tests/test_assets/python_rail/llm_output_3_succeed_gd_and_pydantic.txt +++ b/tests/integration_tests/test_assets/python_rail/llm_output_3_succeed_gd_and_pydantic.txt @@ -11,8 +11,8 @@ "is_sequel": false, "website": "https://www.inceptionmovie.com", "contact_email": "info@inceptionmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 829895144.0, "opening_weekend": 62785337.0 } @@ -28,8 +28,8 @@ "is_sequel": true, "website": "https://www.thedarkknightmovie.com", "contact_email": "info@thedarkknightmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 1004558444.0, "opening_weekend": 158411483.0 } @@ -45,8 +45,8 @@ "is_sequel": true, "website": "https://www.thedarkknightrises.com", "contact_email": "info@thedarkknightrises.com", - "revenue_type": "streaming", - "streaming": { + "revenue": { + "revenue_type": "streaming", "subscriptions": 15000000, "subscription_fee": 9.99 } @@ -62,8 +62,8 @@ "is_sequel": false, "website": "https://www.interstellarmovie.com", "contact_email": "info@interstellarmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 677471339.0, "opening_weekend": 47510360.0 } @@ -79,12 +79,12 @@ "is_sequel": false, "website": "https://www.dunkirkmovie.com", "contact_email": "info@dunkirkmovie.com", - "revenue_type": "box_office", - "box_office": { + "revenue": { + "revenue_type": "box_office", "gross": 526940665.0, "opening_weekend": 50513488.0 } } } ] -} +} \ No newline at end of file diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism.rail b/tests/integration_tests/test_assets/python_rail/validator_parallelism.rail new file mode 100644 index 000000000..6e56a6bfc --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism.rail @@ -0,0 +1,20 @@ + + + + +Say hullo to my little friend + +${gr.complete_string_suffix} + + + \ No newline at end of file diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_1.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_1.txt new file mode 100644 index 000000000..6ebf9f2ed --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_1.txt @@ -0,0 +1,2 @@ +Hello a you +and me \ No newline at end of file diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_2.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_2.txt new file mode 100644 index 000000000..3af4fedeb --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_2.txt @@ -0,0 +1 @@ +hi theremynameispete \ No newline at end of file diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_3.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_3.txt new file mode 100644 index 000000000..5b28462ec --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_3.txt @@ -0,0 +1 @@ +hi apete \ No newline at end of file diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_1.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_1.txt new file mode 100644 index 000000000..de1efb894 --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_1.txt @@ -0,0 +1,16 @@ + +Say hullo to my little friend + + + + +Your generated response should satisfy the following properties: +- two-words +- lower-case +- one-line +- valid-url +- valid-choices: choices=a +- length: min=1 max=10 + +Don't talk; just go. + diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_2.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_2.txt new file mode 100644 index 000000000..e5b1ca7ff --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_2.txt @@ -0,0 +1,23 @@ +This was a previous response you generated: + +====== +Hello a you +and me +====== + +Generate a new response that corrects your old response such that the following issues are fixed +- must be exactly two words +- Value Hello a you +and me is not lower case. + + + +Your generated response should satisfy the following properties: +- two-words +- lower-case +- one-line +- valid-url +- valid-choices: choices=a +- length: min=1 max=10 + +Don't talk; just go. diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_3.txt b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_3.txt new file mode 100644 index 000000000..d499ef665 --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_prompt_3.txt @@ -0,0 +1,20 @@ +This was a previous response you generated: + +====== +hi theremynameispete +====== + +Generate a new response that corrects your old response such that the following issues are fixed +- Value has length greater than 10. Please return a shorter output, that is shorter than 10 characters. + + + +Your generated response should satisfy the following properties: +- two-words +- lower-case +- one-line +- valid-url +- valid-choices: choices=a +- length: min=1 max=10 + +Don't talk; just go. diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_1.py b/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_1.py new file mode 100644 index 000000000..a7de54a7d --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_1.py @@ -0,0 +1,18 @@ +from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import FailResult + +VALIDATOR_PARALLELISM_REASK_1 = FieldReAsk( + incorrect_value="Hello a you\nand me", + fail_results=[ + FailResult( + outcome="fail", + error_message="must be exactly two words", + fix_value="Hello a", + ), + FailResult( + outcome="fail", + error_message="Value Hello a you\nand me is not lower case.", + fix_value="hello a you\nand me", + ), + ], +) diff --git a/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_2.py b/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_2.py new file mode 100644 index 000000000..0ef838eee --- /dev/null +++ b/tests/integration_tests/test_assets/python_rail/validator_parallelism_reask_2.py @@ -0,0 +1,16 @@ +from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import FailResult + +VALIDATOR_PARALLELISM_REASK_2 = FieldReAsk( + incorrect_value="hi theremynameispete", + fail_results=[ + FailResult( + outcome="fail", + metadata=None, + error_message="Value has length greater than 10. " + "Please return a shorter output, " + "that is shorter than 10 characters.", + fix_value="hi theremy", + ) + ], +) diff --git a/tests/integration_tests/test_assets/string/__init__.py b/tests/integration_tests/test_assets/string/__init__.py index 4b65f8b70..3b11a79e1 100644 --- a/tests/integration_tests/test_assets/string/__init__.py +++ b/tests/integration_tests/test_assets/string/__init__.py @@ -1,6 +1,7 @@ # flake8: noqa: E501 import os +from .msg_validated_output_reask import MSG_VALIDATED_OUTPUT_REASK from .validated_output_reask import VALIDATED_OUTPUT_REASK DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__))) @@ -17,6 +18,16 @@ RAIL_SPEC_FOR_STRING_REASK = reader("string_reask.rail") LLM_OUTPUT_REASK = reader("llm_output_reask.txt") +RAIL_SPEC_FOR_MSG_HISTORY = reader("message_history.rail") +MSG_LLM_OUTPUT_INCORRECT = reader("message_history_output.txt") +MSG_LLM_OUTPUT_CORRECT = reader("message_history_reask_output.txt") +MOVIE_MSG_HISTORY = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Can you give me your favorite movie?"}, +] +MSG_COMPILED_PROMPT_REASK = reader("msg_compiled_prompt_reask.txt") +MSG_COMPILED_INSTRUCTIONS_REASK = reader("msg_compiled_instructions_reask.txt") + __all__ = [ "COMPILED_PROMPT", "LLM_OUTPUT", @@ -25,4 +36,11 @@ "RAIL_SPEC_FOR_STRING_REASK", "LLM_OUTPUT_REASK", "VALIDATED_OUTPUT_REASK", + "RAIL_SPEC_FOR_MSG_HISTORY", + "MSG_LLM_OUTPUT_INCORRECT", + "MSG_LLM_OUTPUT_CORRECT", + "MOVIE_MSG_HISTORY", + "MSG_COMPILED_PROMPT_REASK", + "MSG_COMPILED_INSTRUCTIONS_REASK", + "MSG_VALIDATED_OUTPUT_REASK", ] diff --git a/tests/integration_tests/test_assets/string/compiled_instructions.txt b/tests/integration_tests/test_assets/string/compiled_instructions.txt index 2f0f947d8..61985cee4 100644 --- a/tests/integration_tests/test_assets/string/compiled_instructions.txt +++ b/tests/integration_tests/test_assets/string/compiled_instructions.txt @@ -2,12 +2,10 @@ You are a helpful assistant, and you are helping me come up with a name for a pizza. -String description, delimited by +++: - -+++ -Name for the pizza -+++ +Here's a description of what I want you to generate: Name for the pizza Your generated response should satisfy the following properties: - two-words +Don't talk; just go. + diff --git a/tests/integration_tests/test_assets/string/compiled_prompt_reask.txt b/tests/integration_tests/test_assets/string/compiled_prompt_reask.txt index c47f4852e..ac791914e 100644 --- a/tests/integration_tests/test_assets/string/compiled_prompt_reask.txt +++ b/tests/integration_tests/test_assets/string/compiled_prompt_reask.txt @@ -1,18 +1,15 @@ +This was a previous response you generated: -I was given the following string, delimited by +++: - -+++ +====== Tomato Cheese Pizza -+++ +====== -It had the following problems: +Generate a new response that corrects your old response such that the following issues are fixed - must be exactly two words -String description, delimited by +++: - -+++ -Name for the pizza -+++ +Here's a description of what I want you to generate: Name for the pizza Your generated response should satisfy the following properties: - two-words + +Don't talk; just go. diff --git a/tests/integration_tests/test_assets/string/message_history.rail b/tests/integration_tests/test_assets/string/message_history.rail new file mode 100644 index 000000000..d42104189 --- /dev/null +++ b/tests/integration_tests/test_assets/string/message_history.rail @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/message_history_output.txt b/tests/integration_tests/test_assets/string/message_history_output.txt new file mode 100644 index 000000000..0a36571a7 --- /dev/null +++ b/tests/integration_tests/test_assets/string/message_history_output.txt @@ -0,0 +1 @@ +The Matrix Reloaded \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/message_history_reask_output.txt b/tests/integration_tests/test_assets/string/message_history_reask_output.txt new file mode 100644 index 000000000..9e0afa5da --- /dev/null +++ b/tests/integration_tests/test_assets/string/message_history_reask_output.txt @@ -0,0 +1 @@ +The Matrix \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/msg_compiled_instructions_reask.txt b/tests/integration_tests/test_assets/string/msg_compiled_instructions_reask.txt new file mode 100644 index 000000000..ac39ad925 --- /dev/null +++ b/tests/integration_tests/test_assets/string/msg_compiled_instructions_reask.txt @@ -0,0 +1 @@ +You are a helpful assistant. \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/msg_compiled_prompt_reask.txt b/tests/integration_tests/test_assets/string/msg_compiled_prompt_reask.txt new file mode 100644 index 000000000..362486c50 --- /dev/null +++ b/tests/integration_tests/test_assets/string/msg_compiled_prompt_reask.txt @@ -0,0 +1,15 @@ +This was a previous response you generated: + +====== +The Matrix Reloaded +====== + +Generate a new response that corrects your old response such that the following issues are fixed +- must be exactly two words + +Here's a description of what I want you to generate: Generate a movie + +Your generated response should satisfy the following properties: +- two-words + +Don't talk; just go. diff --git a/tests/integration_tests/test_assets/string/msg_validated_output_reask.py b/tests/integration_tests/test_assets/string/msg_validated_output_reask.py new file mode 100644 index 000000000..7a2f31cda --- /dev/null +++ b/tests/integration_tests/test_assets/string/msg_validated_output_reask.py @@ -0,0 +1,12 @@ +from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import FailResult + +MSG_VALIDATED_OUTPUT_REASK = FieldReAsk( + incorrect_value="The Matrix Reloaded", + fail_results=[ + FailResult( + error_message="must be exactly two words", + fix_value="The Matrix", + ) + ], +) diff --git a/tests/integration_tests/test_assets/string/string.rail b/tests/integration_tests/test_assets/string/string.rail index 0627eddd0..7f20dd9e0 100644 --- a/tests/integration_tests/test_assets/string/string.rail +++ b/tests/integration_tests/test_assets/string/string.rail @@ -7,13 +7,13 @@ You are a helpful assistant, and you are helping me come up with a name for a pizza. -@complete_string_suffix +${complete_string_suffix} Given the following ingredients, what would you call this pizza? -{{ingredients}} +${ingredients} \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/string_reask.rail b/tests/integration_tests/test_assets/string/string_reask.rail index 72e1efd7a..98c63852b 100644 --- a/tests/integration_tests/test_assets/string/string_reask.rail +++ b/tests/integration_tests/test_assets/string/string_reask.rail @@ -9,13 +9,13 @@ You are a helpful assistant, and you are helping me come up with a name for a pizza. -@complete_string_suffix +${gr.complete_string_suffix} Given the following ingredients, what would you call this pizza? -{{ingredients}} +${ingredients} \ No newline at end of file diff --git a/tests/integration_tests/test_assets/string/validated_output_reask.py b/tests/integration_tests/test_assets/string/validated_output_reask.py index 555a28414..85557fed2 100644 --- a/tests/integration_tests/test_assets/string/validated_output_reask.py +++ b/tests/integration_tests/test_assets/string/validated_output_reask.py @@ -1,7 +1,12 @@ from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import FailResult VALIDATED_OUTPUT_REASK = FieldReAsk( incorrect_value="Tomato Cheese Pizza", - error_message="must be exactly two words", - fix_value="Tomato Cheese", + fail_results=[ + FailResult( + error_message="must be exactly two words", + fix_value="Tomato Cheese", + ) + ], ) diff --git a/tests/integration_tests/test_async.py b/tests/integration_tests/test_async.py index f842f50ed..9746c5d74 100644 --- a/tests/integration_tests/test_async.py +++ b/tests/integration_tests/test_async.py @@ -8,12 +8,17 @@ @pytest.mark.asyncio -async def test_entity_extraction_with_reask(mocker): +@pytest.mark.parametrize("multiprocessing_validators", (True, False)) +async def test_entity_extraction_with_reask(mocker, multiprocessing_validators: bool): """Test that the entity extraction works with re-asking.""" mocker.patch( "guardrails.llm_providers.async_openai_wrapper", new=async_openai_completion_create, ) + mocker.patch( + "guardrails.validators.Validator.run_in_separate_process", + new=multiprocessing_validators, + ) content = gd.docs_utils.read_pdf("docs/examples/data/chase_card_agreement.pdf") guard = gd.Guard.from_rail_string(entity_extraction.RAIL_SPEC_WITH_REASK) diff --git a/tests/integration_tests/test_cli.py b/tests/integration_tests/test_cli.py index cb152f24b..e6e0fd64f 100644 --- a/tests/integration_tests/test_cli.py +++ b/tests/integration_tests/test_cli.py @@ -20,9 +20,9 @@ Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's information. -{{doctors_notes}} +${doctors_notes} -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2} """ diff --git a/tests/integration_tests/test_data_validation.py b/tests/integration_tests/test_data_validation.py index 2ecdc2f92..dd5abd9b6 100644 --- a/tests/integration_tests/test_data_validation.py +++ b/tests/integration_tests/test_data_validation.py @@ -1,49 +1,43 @@ # flake8: noqa: E501 -from typing import Optional +from typing import Literal, Optional, Union import pytest from pydantic import BaseModel, Field from guardrails import Guard +from guardrails.utils.reask_utils import ReAsk from guardrails.validators import ValidChoices +test_cases = [ + ('{"choice": {"action": "fight", "fight_move": "kick"}}', False), + ( + '{"choice": {"action": "flight", "flight_direction": "north", "flight_speed": 1}}', + False, + ), + ('{"choice": {"action": "flight", "fight_move": "punch"}}', True), + ( + '{"choice": {"action": "fight", "flight_direction": "north", "flight_speed": 1}}', + True, + ), + ('{"choice": {"action": "random_action"}}', True), + ('{"choice": {"action": "fight", "fight": "random_move"}}', True), + ('{"choice": {"action": "flight", "random_key": "random_value"}', True), +] -@pytest.mark.parametrize( - "llm_output, raises", - [ - ('{"action": "fight", "fight": "kick"}', False), - ( - '{"action": "flight", "flight": {"flight_direction": "north", "flight_speed": 1}}', - False, - ), - ('{"action": "flight", "fight": "punch"}', True), - ( - '{"action": "fight", "flight": {"flight_direction": "north", "flight_speed": 1}}', - True, - ), - ('{"action": "random_action"}', True), - ('{"action": "fight", "fight": "random_move"}', True), - ('{"action": "flight", "flight": {"random_key": "random_value"}}', True), - ( - '{"action": "flight", "flight": {"flight_direction": "north", "flight_speed": 1}, "fight": "punch"}', - True, - ), - ], -) + +@pytest.mark.parametrize("llm_output, raises", test_cases) def test_choice_validation(llm_output, raises): rail_spec = """ - + - - - - + + @@ -59,36 +53,27 @@ def test_choice_validation(llm_output, raises): # If raises is True, then the test should raise an exception. if raises: - with pytest.raises(Exception): - guard.parse(llm_output) + with pytest.raises(ValueError): + result = guard.parse(llm_output, num_reasks=0) + if result is None or isinstance(result, ReAsk): + raise ValueError("Expected a result, but got None or ReAsk.") else: - guard.parse(llm_output) - - -@pytest.mark.parametrize( - "llm_output, raises", - [ - ('{"action": "fight", "fight": "kick"}', False), - ( - '{"action": "flight", "flight": {"flight_direction": "north", "flight_speed": 1}}', - False, - ), - ('{"action": "flight", "fight": "punch"}', True), - ( - '{"action": "fight", "flight": {"flight_direction": "north", "flight_speed": 1}}', - True, - ), - ('{"action": "random_action"}', True), - ('{"action": "fight", "fight": "random_move"}', True), - ('{"action": "flight", "flight": {"random_key": "random_value"}}', True), - ( - '{"action": "flight", "flight": {"flight_direction": "north", "flight_speed": 1}, "fight": "punch"}', - True, - ), - ], -) + result = guard.parse(llm_output, num_reasks=0) + assert not isinstance(result, ReAsk) + + +@pytest.mark.parametrize("llm_output, raises", test_cases) def test_choice_validation_pydantic(llm_output, raises): - class FlightDetails(BaseModel): + class Fight(BaseModel): + action: Literal["fight"] + fight_move: str = Field( + validators=ValidChoices( + choices=["punch", "kick", "headbutt"], on_fail="exception" + ) + ) + + class Flight(BaseModel): + action: Literal["flight"] flight_direction: str = Field( validators=ValidChoices( choices=["north", "south", "east", "west"], on_fail="exception" @@ -98,19 +83,17 @@ class FlightDetails(BaseModel): validators=ValidChoices(choices=[1, 2, 3, 4], on_fail="exception") ) - class Action(BaseModel): - action: str = Field(validators=ValidChoices(choices=["fight", "flight"])) - fight: Optional[str] = Field( - validators=ValidChoices(choices=["punch", "kick"], on_fail="exception"), - when="action", - ) - flight: Optional[FlightDetails] = Field(when="action") + class Choice(BaseModel): + choice: Union[Fight, Flight] = Field(..., discriminator="action") - guard = Guard.from_pydantic(output_class=Action, prompt="Dummy prompt.") + guard = Guard.from_pydantic(output_class=Choice, prompt="Dummy prompt.") # If raises is True, then the test should raise an exception. if raises: - with pytest.raises(Exception): - guard.parse(llm_output) + with pytest.raises(ValueError): + result = guard.parse(llm_output, num_reasks=0) + if result is None or isinstance(result, ReAsk): + raise ValueError("Expected a result, but got None or ReAsk.") else: - guard.parse(llm_output) + result = guard.parse(llm_output, num_reasks=0) + assert not isinstance(result, ReAsk) diff --git a/tests/integration_tests/test_embedding_openai.py b/tests/integration_tests/test_embedding_openai.py index 3821c1d64..eae38f340 100644 --- a/tests/integration_tests/test_embedding_openai.py +++ b/tests/integration_tests/test_embedding_openai.py @@ -1,10 +1,40 @@ import os +from unittest.mock import Mock, patch import pytest from guardrails.embedding import OpenAIEmbedding +class MockOpenAIEmbedding: + def __init__( + self, + model=None, + encoding_name=None, + max_tokens=None, + api_key=None, + api_base=None, + ): + pass + + def _len_safe_get_embedding(self, text, embedder, average=True): + return [1.0, 2.0, 3.0] + + +class MockResponse: + def __init__(self, data=None): + self.data = data or [] + + def json(self): + return {"data": self.data} + + +@pytest.fixture +def mock_openai_embedding(monkeypatch): + monkeypatch.setattr("openai.Embedding.create", MockOpenAIEmbedding()) + return MockOpenAIEmbedding + + @pytest.mark.skipif( os.environ.get("OPENAI_API_KEY") is None, reason="openai api key not set" ) @@ -19,3 +49,58 @@ def test_embedding_query(self): e = OpenAIEmbedding() result = e.embed_query("foo") assert len(result) == 1536 + + def test_embed_query(self, mock_openai_embedding): + instance = OpenAIEmbedding() + instance._get_embedding = Mock(return_value=[[1.0, 2.0, 3.0]]) + result = instance.embed_query("test query") + assert result == [1.0, 2.0, 3.0] + + @patch("os.environ.get", return_value="test_api_key") + @patch("openai.Embedding.create", return_value=MockResponse(data=[[1.0, 2.0, 3.0]])) + def test__get_embedding(self, mock_create, mock_get_env): + instance = OpenAIEmbedding(api_key="test_api_key") + result = instance._get_embedding(["test text"]) + assert result == [[1.0, 2.0, 3.0]] + mock_create.assert_called_once_with( + api_key="test_api_key", + model="text-embedding-ada-002", + input=["test text"], + api_base=None, + ) + + +@pytest.fixture +def openai_embeddings_instance(): + # You can customize this fixture creation based on your actual class initialization + return OpenAIEmbedding("text-embedding-ada-002") # Initialize with a model name + + +def test_output_dim_for_text_embedding_ada_002(openai_embeddings_instance): + assert openai_embeddings_instance.output_dim == 1536 + + +def test_output_dim_for_ada_model(openai_embeddings_instance): + openai_embeddings_instance._model = "some-ada-model" + assert openai_embeddings_instance.output_dim == 1024 + + +def test_output_dim_for_babbage_model(openai_embeddings_instance): + openai_embeddings_instance._model = "some-babbage-model" + assert openai_embeddings_instance.output_dim == 2048 + + +def test_output_dim_for_curie_model(openai_embeddings_instance): + openai_embeddings_instance._model = "some-curie-model" + assert openai_embeddings_instance.output_dim == 4096 + + +def test_output_dim_for_davinci_model(openai_embeddings_instance): + openai_embeddings_instance._model = "some-davinci-model" + assert openai_embeddings_instance.output_dim == 12288 + + +def test_output_dim_for_unknown_model(openai_embeddings_instance): + openai_embeddings_instance._model = "unknown-model" + with pytest.raises(ValueError): + openai_embeddings_instance.output_dim diff --git a/tests/integration_tests/test_guard.py b/tests/integration_tests/test_guard.py index 525d47c42..e12efb3d4 100644 --- a/tests/integration_tests/test_guard.py +++ b/tests/integration_tests/test_guard.py @@ -1,3 +1,4 @@ +import json from typing import Optional, Union import openai @@ -7,13 +8,14 @@ import guardrails as gd from guardrails.guard import Guard from guardrails.utils.reask_utils import FieldReAsk +from guardrails.validators import FailResult from .mock_llm_outputs import ( entity_extraction, openai_chat_completion_create, openai_completion_create, ) -from .test_assets import string +from .test_assets import pydantic, string @pytest.fixture(scope="module") @@ -39,7 +41,7 @@ def rail_spec(): Generate a JSON of dummy data, where the data types are specified by the user. -@complete_json_suffix +${gr.complete_json_suffix} @@ -96,24 +98,41 @@ def guard_initializer( return Guard.from_pydantic(rail, prompt=prompt, instructions=instructions) -def test_rail_spec_output_parse(rail_spec, llm_output, validated_output): +'''def test_rail_spec_output_parse(rail_spec, llm_output, validated_output): """Test that the rail_spec fixture is working.""" guard = gd.Guard.from_rail_string(rail_spec) - assert guard.parse(llm_output) == validated_output + assert guard.parse(llm_output) == validated_output''' @pytest.mark.parametrize( - "rail,prompt", + "rail,prompt,test_full_schema_reask", [ - (entity_extraction.RAIL_SPEC_WITH_REASK, None), - (entity_extraction.PYDANTIC_RAIL_WITH_REASK, entity_extraction.PYDANTIC_PROMPT), + (entity_extraction.RAIL_SPEC_WITH_REASK, None, False), + (entity_extraction.RAIL_SPEC_WITH_REASK, None, True), + ( + entity_extraction.PYDANTIC_RAIL_WITH_REASK, + entity_extraction.PYDANTIC_PROMPT, + False, + ), + ( + entity_extraction.PYDANTIC_RAIL_WITH_REASK, + entity_extraction.PYDANTIC_PROMPT, + True, + ), ], ) -def test_entity_extraction_with_reask(mocker, rail, prompt): +@pytest.mark.parametrize("multiprocessing_validators", (True, False)) +def test_entity_extraction_with_reask( + mocker, rail, prompt, test_full_schema_reask, multiprocessing_validators +): """Test that the entity extraction works with re-asking.""" mocker.patch( "guardrails.llm_providers.openai_wrapper", new=openai_completion_create ) + mocker.patch( + "guardrails.validators.Validator.run_in_separate_process", + new=multiprocessing_validators, + ) content = gd.docs_utils.read_pdf("docs/examples/data/chase_card_agreement.pdf") guard = guard_initializer(rail, prompt) @@ -122,6 +141,8 @@ def test_entity_extraction_with_reask(mocker, rail, prompt): llm_api=openai.Completion.create, prompt_params={"document": content[:6000]}, num_reasks=1, + max_tokens=2000, + full_schema_reask=test_full_schema_reask, ) # Assertions are made on the guard state object. @@ -142,22 +163,34 @@ def test_entity_extraction_with_reask(mocker, rail, prompt): # For reask validator logs nested_validator_log = ( guard_history[0] - .field_validation_logs["fees"] + .field_validation_logs.children["fees"] .children[1] .children["name"] .validator_logs[1] ) + assert nested_validator_log.value_before_validation == "my chase plan" assert nested_validator_log.value_after_validation == FieldReAsk( incorrect_value="my chase plan", - fix_value="my chase", - error_message="must be exactly two words", + fail_results=[ + FailResult( + fix_value="my chase", + error_message="must be exactly two words", + ) + ], path=["fees", 1, "name"], ) # For re-asked prompt and output - assert guard_history[1].prompt == gd.Prompt(entity_extraction.COMPILED_PROMPT_REASK) - assert guard_history[1].output == entity_extraction.LLM_OUTPUT_REASK + if test_full_schema_reask: + assert ( + guard_history[1].prompt.source + == entity_extraction.COMPILED_PROMPT_FULL_REASK + ) + assert guard_history[1].output == entity_extraction.LLM_OUTPUT_FULL_REASK + else: + assert guard_history[1].prompt.source == entity_extraction.COMPILED_PROMPT_REASK + assert guard_history[1].output == entity_extraction.LLM_OUTPUT_REASK assert ( guard_history[1].validated_output == entity_extraction.VALIDATED_OUTPUT_REASK_2 ) @@ -488,6 +521,17 @@ def test_skeleton_reask(mocker): entity_extraction.COMPILED_PROMPT_REASK, entity_extraction.COMPILED_INSTRUCTIONS_REASK, ), + ( + entity_extraction.RAIL_SPEC_WITH_REASK_NO_PROMPT, + None, + None, + entity_extraction.OPTIONAL_MSG_HISTORY, + openai.ChatCompletion.create, + None, + None, + entity_extraction.COMPILED_PROMPT_REASK, + entity_extraction.COMPILED_INSTRUCTIONS_REASK, + ), ], ) def test_entity_extraction_with_reask_with_optional_prompts( @@ -514,14 +558,13 @@ def test_entity_extraction_with_reask_with_optional_prompts( ) content = gd.docs_utils.read_pdf("docs/examples/data/chase_card_agreement.pdf") - # guard = guard_initializer(rail, prompt) guard = Guard.from_rail_string(rail) _, final_output = guard( llm_api=llm_api, prompt=prompt, instructions=instructions, - chat_history=history, + msg_history=history, prompt_params={"document": content[:6000]}, num_reasks=1, ) @@ -535,18 +578,25 @@ def test_entity_extraction_with_reask_with_optional_prompts( assert len(guard_history) == 2 # For orginal prompt and output - assert guard_history[0].prompt == gd.Prompt(expected_prompt) + expected_prompt = ( + gd.Prompt(expected_prompt) if expected_prompt is not None else None + ) + assert guard_history[0].prompt == expected_prompt assert guard_history[0].output == entity_extraction.LLM_OUTPUT assert ( guard_history[0].validated_output == entity_extraction.VALIDATED_OUTPUT_REASK_1 ) - if expected_instructions: - assert guard_history[0].instructions == gd.Instructions(expected_instructions) + expected_instructions = ( + gd.Instructions(expected_instructions) + if expected_instructions is not None + else None + ) + assert guard_history[0].instructions == expected_instructions # For reask validator logs nested_validator_log = ( guard_history[0] - .field_validation_logs["fees"] + .field_validation_logs.children["fees"] .children[1] .children["name"] .validator_logs[1] @@ -554,14 +604,19 @@ def test_entity_extraction_with_reask_with_optional_prompts( assert nested_validator_log.value_before_validation == "my chase plan" assert nested_validator_log.value_after_validation == FieldReAsk( incorrect_value="my chase plan", - fix_value="my chase", - error_message="must be exactly two words", + fail_results=[ + FailResult( + fix_value="my chase", + error_message="must be exactly two words", + ) + ], path=["fees", 1, "name"], ) # For re-asked prompt and output assert guard_history[1].prompt == gd.Prompt(expected_reask_prompt) assert guard_history[1].output == entity_extraction.LLM_OUTPUT_REASK + assert ( guard_history[1].validated_output == entity_extraction.VALIDATED_OUTPUT_REASK_2 ) @@ -569,3 +624,79 @@ def test_entity_extraction_with_reask_with_optional_prompts( assert guard_history[1].instructions == gd.Instructions( expected_reask_instructions ) + + +def test_string_with_message_history_reask(mocker): + """Test single string (non-JSON) generation with message history and + reask.""" + mocker.patch( + "guardrails.llm_providers.openai_chat_wrapper", + new=openai_chat_completion_create, + ) + + guard = gd.Guard.from_rail_string(string.RAIL_SPEC_FOR_MSG_HISTORY) + _, final_output = guard( + llm_api=openai.ChatCompletion.create, + msg_history=string.MOVIE_MSG_HISTORY, + temperature=0.0, + model="gpt-3.5-turbo", + ) + + assert final_output == string.MSG_LLM_OUTPUT_CORRECT + + guard_history = guard.guard_state.most_recent_call.history + + # Check that the guard state object has the correct number of re-asks. + assert len(guard_history) == 2 + + assert guard_history[0].instructions is None + assert guard_history[0].prompt is None + assert guard_history[0].output == string.MSG_LLM_OUTPUT_INCORRECT + assert guard_history[0].validated_output == string.MSG_VALIDATED_OUTPUT_REASK + + # For re-asked prompt and output + assert guard_history[1].prompt == gd.Prompt(string.MSG_COMPILED_PROMPT_REASK) + assert guard_history[1].instructions == gd.Instructions( + string.MSG_COMPILED_INSTRUCTIONS_REASK + ) + assert guard_history[1].output == string.MSG_LLM_OUTPUT_CORRECT + assert guard_history[1].validated_output == string.MSG_LLM_OUTPUT_CORRECT + + +def test_pydantic_with_message_history_reask(mocker): + """Test JSON generation with message history re-asking.""" + mocker.patch( + "guardrails.llm_providers.openai_chat_wrapper", + new=openai_chat_completion_create, + ) + + guard = gd.Guard.from_pydantic(output_class=pydantic.WITH_MSG_HISTORY) + raw_output, guarded_output = guard( + llm_api=openai.ChatCompletion.create, + msg_history=string.MOVIE_MSG_HISTORY, + temperature=0.0, + model="gpt-3.5-turbo", + ) + + assert raw_output == pydantic.MSG_HISTORY_LLM_OUTPUT_CORRECT + assert guarded_output == json.loads(pydantic.MSG_HISTORY_LLM_OUTPUT_CORRECT) + + guard_history = guard.guard_state.most_recent_call.history + + # Check that the guard state object has the correct number of re-asks. + assert len(guard_history) == 2 + + assert guard_history[0].instructions is None + assert guard_history[0].prompt is None + assert guard_history[0].output == pydantic.MSG_HISTORY_LLM_OUTPUT_INCORRECT + assert guard_history[0].validated_output == pydantic.MSG_VALIDATED_OUTPUT_REASK + + # For re-asked prompt and output + assert guard_history[1].prompt == gd.Prompt(pydantic.MSG_COMPILED_PROMPT_REASK) + assert guard_history[1].instructions == gd.Instructions( + pydantic.MSG_COMPILED_INSTRUCTIONS_REASK + ) + assert guard_history[1].output == pydantic.MSG_HISTORY_LLM_OUTPUT_CORRECT + assert guard_history[1].validated_output == json.loads( + pydantic.MSG_HISTORY_LLM_OUTPUT_CORRECT + ) diff --git a/tests/integration_tests/test_json_utils.py b/tests/integration_tests/test_json_utils.py new file mode 100644 index 000000000..3509efca7 --- /dev/null +++ b/tests/integration_tests/test_json_utils.py @@ -0,0 +1,126 @@ +import pytest + +from guardrails.utils.json_utils import ( + ChoicePlaceholder, + DictPlaceholder, + ListPlaceholder, + ValuePlaceholder, +) + + +@pytest.mark.parametrize( + "optional,type_string,value,coerce_types,expected_value", + [ + (False, "integer", None, True, ValuePlaceholder.VerificationFailed), + (False, "integer", None, False, ValuePlaceholder.VerificationFailed), + ], +) +def test_value_placeholder_verify( + optional, type_string, value, coerce_types, expected_value +): + ph = ValuePlaceholder(optional, type_string) + + verified_type = ph.verify(value, False, coerce_types) + + assert verified_type == expected_value + + +@pytest.mark.parametrize( + "optional,children,value,coerce_types,expected_value", + [ + (True, {}, None, True, True), + (False, {}, None, False, False), + ( + False, + {"child": ValuePlaceholder(False, "integer")}, + {"child": None}, + False, + False, + ), + ], +) +def test_dict_placeholder_verify( + optional, children, value, coerce_types, expected_value +): + ph = DictPlaceholder(optional, children) + + verified_type = ph.verify(value, False, coerce_types) + + assert verified_type == expected_value + + +@pytest.mark.parametrize( + "optional,children,value,coerce_types,expected_value", + [ + (True, None, None, True, True), + (False, None, None, False, False), + (False, ValuePlaceholder(False, "integer"), [None], False, False), + ], +) +def test_list_placeholder_verify( + optional, children, value, coerce_types, expected_value +): + ph = ListPlaceholder(optional, children) + + verified_type = ph.verify(value, False, coerce_types) + + assert verified_type == expected_value + + +@pytest.mark.parametrize( + "optional,cases,value,coerce_types,expected_value", + [ + (True, {}, None, True, True), + (False, {}, None, False, False), + (False, {}, {}, False, False), + (False, {}, {"discriminator": None}, False, False), + ], +) +def test_choice_placeholder_verify( + optional, cases, value, coerce_types, expected_value +): + ph = ChoicePlaceholder(optional, "discriminator", cases) + + verified_type = ph.verify(value, False, coerce_types) + + assert verified_type == expected_value + + +@pytest.mark.parametrize( + "optional,cases,value,coerce_types,expected_value", + [ + ( + False, + {"discriminator": "abc", "abc": ValuePlaceholder(False, "integer")}, + {"discriminator": "abc", "abc": None}, + False, + "Choice cases must be objects", + ), + ( + False, + { + "discriminator": "abc", + "abc": DictPlaceholder( + False, {"discriminator": ValuePlaceholder(False, "integer")} + ), + }, + {"discriminator": "abc", "abc": {"discriminator": 1}}, + False, + "Choice cases must be objects", + ), + ], +) +def test_choice_placeholder_verify_raises( + optional, cases, value, coerce_types, expected_value +): + with pytest.raises(ValueError) as error: + ph = ChoicePlaceholder(optional, "discriminator", cases) + + return_value = ph.verify(value, False, coerce_types) + + print("return_value: ", return_value) + + import traceback + + traceback.print_exception(error) + assert str(error) == expected_value diff --git a/tests/integration_tests/test_multi_reask.py b/tests/integration_tests/test_multi_reask.py new file mode 100644 index 000000000..4dfebfa98 --- /dev/null +++ b/tests/integration_tests/test_multi_reask.py @@ -0,0 +1,47 @@ +import openai + +import guardrails as gd + +from .mock_llm_outputs import openai_completion_create +from .test_assets import python_rail + + +def test_multi_reask(mocker): + """Test that parallel reasking works.""" + mocker.patch( + "guardrails.llm_providers.openai_wrapper", new=openai_completion_create + ) + + guard = gd.Guard.from_rail_string(python_rail.RAIL_SPEC_WITH_VALIDATOR_PARALLELISM) + + _, final_output = guard( + llm_api=openai.Completion.create, + engine="text-davinci-003", + num_reasks=5, + ) + + # Assertions are made on the guard state object. + # assert final_output == python_rail + + guard_history = guard.guard_state.most_recent_call.history + + assert len(guard_history) == 3 + + assert guard_history[0].prompt.source == python_rail.VALIDATOR_PARALLELISM_PROMPT_1 + assert guard_history[0].output == python_rail.VALIDATOR_PARALLELISM_RESPONSE_1 + assert ( + guard_history[0].validated_output == python_rail.VALIDATOR_PARALLELISM_REASK_1 + ) + + assert guard_history[1].prompt.source == python_rail.VALIDATOR_PARALLELISM_PROMPT_2 + assert guard_history[1].output == python_rail.VALIDATOR_PARALLELISM_RESPONSE_2 + assert ( + guard_history[1].validated_output == python_rail.VALIDATOR_PARALLELISM_REASK_2 + ) + + assert guard_history[2].prompt.source == python_rail.VALIDATOR_PARALLELISM_PROMPT_3 + assert guard_history[2].output == python_rail.VALIDATOR_PARALLELISM_RESPONSE_3 + assert ( + guard_history[2].validated_output + == python_rail.VALIDATOR_PARALLELISM_RESPONSE_3 + ) diff --git a/tests/integration_tests/test_pydantic.py b/tests/integration_tests/test_pydantic.py index ff9770b4b..3b7801442 100644 --- a/tests/integration_tests/test_pydantic.py +++ b/tests/integration_tests/test_pydantic.py @@ -3,6 +3,7 @@ import guardrails as gd from .mock_llm_outputs import openai_completion_create, pydantic +from .test_assets.pydantic import VALIDATED_RESPONSE_REASK_PROMPT, ListOfPeople def test_pydantic_with_reask(mocker): @@ -11,13 +12,14 @@ def test_pydantic_with_reask(mocker): "guardrails.llm_providers.openai_wrapper", new=openai_completion_create ) - guard = gd.Guard.from_rail_string(pydantic.RAIL_SPEC_WITH_REASK) + guard = gd.Guard.from_pydantic(ListOfPeople, prompt=VALIDATED_RESPONSE_REASK_PROMPT) _, final_output = guard( openai.Completion.create, engine="text-davinci-003", max_tokens=512, temperature=0.5, num_reasks=2, + full_schema_reask=False, ) # Assertions are made on the guard state object. diff --git a/tests/integration_tests/test_python_rail.py b/tests/integration_tests/test_python_rail.py index 6803b3796..dcc98d49f 100644 --- a/tests/integration_tests/test_python_rail.py +++ b/tests/integration_tests/test_python_rail.py @@ -1,6 +1,6 @@ import json from datetime import date, time -from typing import List, Optional +from typing import List, Literal, Union import openai import pytest @@ -9,20 +9,22 @@ import guardrails as gd from guardrails.utils.pydantic_utils import add_validator from guardrails.validators import ( - EventDetail, + FailResult, + PassResult, + TwoWords, + ValidationResult, Validator, - ValidChoices, ValidLength, register_validator, ) -from .mock_llm_outputs import openai_chat_completion_create -from .test_assets import python_rail +from .mock_llm_outputs import openai_chat_completion_create, openai_completion_create +from .test_assets import python_rail, string @register_validator(name="is-valid-director", data_type="string") class IsValidDirector(Validator): - def validate(self, key, value, schema) -> dict: + def validate(self, value, metadata) -> ValidationResult: valid_names = [ "Christopher Nolan", "Steven Spielberg", @@ -31,15 +33,11 @@ def validate(self, key, value, schema) -> dict: "James Cameron", ] if value not in valid_names: - raise EventDetail( - key, - value, - schema, - f"Value {value} is not a valid director name. " + return FailResult( + error_message=f"Value {value} is not a valid director name. " f"Valid choices are {valid_names}.", - None, ) - return schema + return PassResult() def test_python_rail(mocker): @@ -49,6 +47,7 @@ def test_python_rail(mocker): ) class BoxOfficeRevenue(BaseModel): + revenue_type: Literal["box_office"] gross: float opening_weekend: float @@ -60,6 +59,7 @@ def validate_gross(cls, gross): return gross class StreamingRevenue(BaseModel): + revenue_type: Literal["streaming"] subscriptions: int subscription_fee: float @@ -70,20 +70,17 @@ class Details(BaseModel): is_sequel: bool = Field(default=False) website: str = Field(validators=[ValidLength(min=9, max=100, on_fail="reask")]) contact_email: str - revenue_type: str = Field( - validators=[ValidChoices(choices=["box_office", "streaming"])] + revenue: Union[BoxOfficeRevenue, StreamingRevenue] = Field( + ..., discriminator="revenue_type" ) - box_office: Optional[BoxOfficeRevenue] = Field(when="revenue_type") - streaming: Optional[StreamingRevenue] = Field(when="revenue_type") # Root-level validation using Pydantic (Not in Guardrails) @root_validator def validate_budget_and_gross(cls, values): budget = values.get("budget") - revenue_type = values.get("revenue_type") - box_office_revenue = values.get("box_office") - if revenue_type == "box_office" and box_office_revenue: - gross = box_office_revenue.gross + revenue = values.get("revenue") + if isinstance(revenue, BoxOfficeRevenue): + gross = revenue.gross if budget >= gross: raise ValueError("Budget must be less than gross revenue") return values @@ -101,11 +98,12 @@ class Director(BaseModel): output_class=Director, prompt=( "Provide detailed information about the top 5 grossing movies from" - " {{director}} including release date, duration, budget, whether " - "it's a sequel, website, and contact email.\n@json_suffix_without_examples" + " ${director} including release date, duration, budget, whether " + "it's a sequel, website, and contact email.\n" + "${gr.json_suffix_without_examples}" ), instructions="\nYou are a helpful assistant only capable of communicating" - " with valid JSON, and no other text.\n@json_suffix_prompt_examples", + " with valid JSON, and no other text.\n${gr.json_suffix_prompt_examples}", ) # Guardrails runs validation and fixes the first failing output through reasking @@ -113,6 +111,7 @@ class Director(BaseModel): openai.ChatCompletion.create, prompt_params={"director": "Christopher Nolan"}, num_reasks=2, + full_schema_reask=False, ) # Assertions are made on the guard state object. @@ -161,6 +160,7 @@ def test_python_rail_add_validator(mocker): ) class BoxOfficeRevenue(BaseModel): + revenue_type: Literal["box_office"] gross: float opening_weekend: float @@ -172,6 +172,7 @@ def validate_gross(cls, gross): return gross class StreamingRevenue(BaseModel): + revenue_type: Literal["streaming"] subscriptions: int subscription_fee: float @@ -182,14 +183,11 @@ class Details(BaseModel): is_sequel: bool = Field(default=False) website: str contact_email: str - revenue_type: str - box_office: Optional[BoxOfficeRevenue] = Field(when="revenue_type") - streaming: Optional[StreamingRevenue] = Field(when="revenue_type") + revenue: Union[BoxOfficeRevenue, StreamingRevenue] = Field( + ..., discriminator="revenue_type" + ) # Register guardrails validators - _revenue_type_validator = add_validator( - "revenue_type", fn=ValidChoices(choices=["box_office", "streaming"]) - ) _website_validator = add_validator( "website", fn=ValidLength(min=9, max=100, on_fail="reask") ) @@ -198,10 +196,9 @@ class Details(BaseModel): @root_validator def validate_budget_and_gross(cls, values): budget = values.get("budget") - revenue_type = values.get("revenue_type") - box_office_revenue = values.get("box_office") - if revenue_type == "box_office" and box_office_revenue: - gross = box_office_revenue.gross + revenue = values.get("revenue") + if isinstance(revenue, BoxOfficeRevenue): + gross = revenue.gross if budget >= gross: raise ValueError("Budget must be less than gross revenue") return values @@ -222,11 +219,12 @@ class Director(BaseModel): output_class=Director, prompt=( "Provide detailed information about the top 5 grossing movies from" - " {{director}} including release date, duration, budget, whether " - "it's a sequel, website, and contact email.\n@json_suffix_without_examples" + " ${director} including release date, duration, budget, whether " + "it's a sequel, website, and contact email.\n" + "${gr.json_suffix_without_examples}" ), instructions="\nYou are a helpful assistant only capable of communicating" - " with valid JSON, and no other text.\n@json_suffix_prompt_examples", + " with valid JSON, and no other text.\n${gr.json_suffix_prompt_examples}", ) # Guardrails runs validation and fixes the first failing output through reasking @@ -234,6 +232,7 @@ class Director(BaseModel): openai.ChatCompletion.create, prompt_params={"director": "Christopher Nolan"}, num_reasks=2, + full_schema_reask=False, ) # Assertions are made on the guard state object. @@ -273,3 +272,54 @@ class Director(BaseModel): # The fixed output should pass validation using Pydantic Director.parse_raw(python_rail.LLM_OUTPUT_3_SUCCEED_GUARDRAILS_AND_PYDANTIC) + + +def test_python_string(mocker): + """Test single string (non-JSON) generation via pydantic with re-asking.""" + mocker.patch( + "guardrails.llm_providers.openai_wrapper", new=openai_completion_create + ) + + validators = [TwoWords(on_fail="reask")] + description = "Name for the pizza" + instructions = """ +You are a helpful assistant, and you are helping me come up with a name for a pizza. + +${gr.complete_string_suffix} +""" + + prompt = """ +Given the following ingredients, what would you call this pizza? + +${ingredients} +""" + + guard = gd.Guard.from_string( + validators, description, prompt=prompt, instructions=instructions + ) + _, final_output = guard( + llm_api=openai.Completion.create, + prompt_params={"ingredients": "tomato, cheese, sour cream"}, + num_reasks=1, + max_tokens=100, + ) + + assert final_output == string.LLM_OUTPUT_REASK + + guard_history = guard.guard_state.most_recent_call.history + + # Check that the guard state object has the correct number of re-asks. + assert len(guard_history) == 2 + + # For orginal prompt and output + assert guard_history[0].instructions == gd.Instructions( + string.COMPILED_INSTRUCTIONS + ) + assert guard_history[0].prompt == gd.Prompt(string.COMPILED_PROMPT) + assert guard_history[0].output == string.LLM_OUTPUT + assert guard_history[0].validated_output == string.VALIDATED_OUTPUT_REASK + + # For re-asked prompt and output + assert guard_history[1].prompt == gd.Prompt(string.COMPILED_PROMPT_REASK) + assert guard_history[1].output == string.LLM_OUTPUT_REASK + assert guard_history[1].validated_output == string.LLM_OUTPUT_REASK diff --git a/tests/integration_tests/test_schema_to_prompt.py b/tests/integration_tests/test_schema_to_prompt.py index 4fe19c9bc..62efa4f81 100644 --- a/tests/integration_tests/test_schema_to_prompt.py +++ b/tests/integration_tests/test_schema_to_prompt.py @@ -44,12 +44,17 @@ def test_choice_schema(): guard = Guard.from_rail_string(rail_spec) schema_2_prompt = guard.output_schema.transpile() expected_schema_2_prompt = """ - - - - - - + + + + + + + + + + + """ assert schema_2_prompt == expected_schema_2_prompt diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit_tests/mock_embeddings.py b/tests/unit_tests/mock_embeddings.py new file mode 100644 index 000000000..576accf85 --- /dev/null +++ b/tests/unit_tests/mock_embeddings.py @@ -0,0 +1,22 @@ +def mock_create_embedding(input, *args, **kwargs): + mocked_embeddings = { + "It was a beautiful day. " "In the afternoon, we drank tea.": [0, 0.5], + "Then we went to the park. " + "There was a lot of people there. " + "A dog was there too.": [0.5, 0], + "It was a nice day.": [0.25, 0.25], + "I went to the park.": [0.25, 0.25], + "I saw a dog.": [0.25, 0.25], + } + + if not isinstance(input, list): + input = [input] + + returns = [] + for text in input: + try: + returns.append({"embedding": mocked_embeddings[text]}) + except KeyError: + print(input) + raise ValueError("Text not found in mocked embeddings") + return {"data": returns} diff --git a/tests/unit_tests/mocks/__init__.py b/tests/unit_tests/mocks/__init__.py new file mode 100644 index 000000000..aaaf15ca9 --- /dev/null +++ b/tests/unit_tests/mocks/__init__.py @@ -0,0 +1,11 @@ +from .mock_async_validator_service import MockAsyncValidatorService +from .mock_loop import MockLoop +from .mock_sequential_validator_service import MockSequentialValidatorService +from .mock_validator import MockValidator + +__all__ = [ + "MockAsyncValidatorService", + "MockSequentialValidatorService", + "MockLoop", + "MockValidator", +] diff --git a/tests/unit_tests/mocks/mock_async_validator_service.py b/tests/unit_tests/mocks/mock_async_validator_service.py new file mode 100644 index 000000000..060f25396 --- /dev/null +++ b/tests/unit_tests/mocks/mock_async_validator_service.py @@ -0,0 +1,12 @@ +import asyncio + + +class MockAsyncValidatorService: + async def async_validate(self, *args): + await asyncio.sleep(0.1) + # The return value doesn't really matter here. + # We just need something to identify which class and method was called. + return "MockAsyncValidatorService.async_validate", {"async": True} + + def validate(self, *args): + return "MockAsyncValidatorService.validate", {"sync": True} diff --git a/tests/unit_tests/mocks/mock_loop.py b/tests/unit_tests/mocks/mock_loop.py new file mode 100644 index 000000000..cf7eb2b67 --- /dev/null +++ b/tests/unit_tests/mocks/mock_loop.py @@ -0,0 +1,12 @@ +class MockLoop: + def __init__(self, loop_is_running: bool): + self.loop_is_running = loop_is_running + + def is_running(self): + return self.loop_is_running + + def run_until_complete(self, future): + return future + + def run_in_executor(self, executor, func, *args): + return func(*args) diff --git a/tests/unit_tests/mocks/mock_sequential_validator_service.py b/tests/unit_tests/mocks/mock_sequential_validator_service.py new file mode 100644 index 000000000..f1656c317 --- /dev/null +++ b/tests/unit_tests/mocks/mock_sequential_validator_service.py @@ -0,0 +1,3 @@ +class MockSequentialValidatorService: + def validate(self, *args): + return "MockSequentialValidatorService.validate", {"sync": True} diff --git a/tests/unit_tests/mocks/mock_validator.py b/tests/unit_tests/mocks/mock_validator.py new file mode 100644 index 000000000..ab103bc27 --- /dev/null +++ b/tests/unit_tests/mocks/mock_validator.py @@ -0,0 +1,28 @@ +from typing import Any, Callable, Dict, Union + +from guardrails import Validator, register_validator +from guardrails.validators import FailResult, PassResult, ValidationResult + + +class MockValidator(Validator): + def __init__( + self, + name: str, + on_fail: Union[str, Callable] = None, + should_pass: bool = True, + return_value: Any = None, + ): + register_validator(name=name, data_type=["string"])(self) + super().__init__(on_fail=on_fail) + self.name = name + self.on_fail = on_fail + self.should_pass = should_pass + self.return_value = return_value + + def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult: + if self.should_pass: + return self.return_value if self.return_value is not None else PassResult() + else: + return FailResult( + error_message="Value is not valid.", + ) diff --git a/tests/unit_tests/test_assets/article1.txt b/tests/unit_tests/test_assets/article1.txt new file mode 100644 index 000000000..a6aacf870 --- /dev/null +++ b/tests/unit_tests/test_assets/article1.txt @@ -0,0 +1 @@ +It was a beautiful day. In the afternoon, we drank tea. \ No newline at end of file diff --git a/tests/unit_tests/test_assets/article2.txt b/tests/unit_tests/test_assets/article2.txt new file mode 100644 index 000000000..c35501647 --- /dev/null +++ b/tests/unit_tests/test_assets/article2.txt @@ -0,0 +1 @@ +Then we went to the park. There was a lot of people there. A dog was there too. \ No newline at end of file diff --git a/tests/unit_tests/test_assets/empty.rail b/tests/unit_tests/test_assets/empty.rail new file mode 100644 index 000000000..d86c0aeda --- /dev/null +++ b/tests/unit_tests/test_assets/empty.rail @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/tests/unit_tests/test_async_validator_service.py b/tests/unit_tests/test_async_validator_service.py new file mode 100644 index 000000000..d4f9faf9d --- /dev/null +++ b/tests/unit_tests/test_async_validator_service.py @@ -0,0 +1,280 @@ +import asyncio + +import pytest + +from guardrails.datatypes import FieldValidation +from guardrails.utils.logs_utils import FieldValidationLogs, ValidatorLogs +from guardrails.validator_service import AsyncValidatorService +from guardrails.validators import PassResult + +from .mocks import MockLoop, MockValidator + +empty_field_validation = FieldValidation( + key="mock-key", value="mock-value", validators=[], children=[] +) +empty_field_validation_logs = FieldValidationLogs(validator_logs=[], children={}) +avs = AsyncValidatorService() + + +def test_validate_with_running_loop(mocker): + with pytest.raises(RuntimeError) as e_info: + mock_loop = MockLoop(True) + mocker.patch("asyncio.get_event_loop", return_value=mock_loop) + avs.validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert ( + str(e_info) + == "Async event loop found, please call `validate_async` instead." + ) + + +def test_validate_without_running_loop(mocker): + mock_loop = MockLoop(False) + mocker.patch("asyncio.get_event_loop", return_value=mock_loop) + async_validate_mock = mocker.MagicMock( + return_value=("async_validate_mock", {"async": True}) + ) + mocker.patch.object(avs, "async_validate", async_validate_mock) + loop_spy = mocker.spy(mock_loop, "run_until_complete") + + validated_value, validated_metadata = avs.validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert loop_spy.call_count == 1 + async_validate_mock.assert_called_once_with( + True, {}, empty_field_validation, empty_field_validation_logs + ) + assert validated_value == "async_validate_mock" + assert validated_metadata == {"async": True} + + +@pytest.mark.asyncio +async def test_async_validate_with_children(mocker): + validate_dependents_mock = mocker.patch.object(avs, "validate_dependents") + + run_validators_mock = mocker.patch.object(avs, "run_validators") + run_validators_mock.return_value = ("run_validators_mock", {"async": True}) + + field_validation = FieldValidation( + key="mock-parent-key", + value="mock-parent-value", + validators=[], + children=[empty_field_validation], + ) + + validated_value, validated_metadata = await avs.async_validate( + value=True, + metadata={}, + validator_setup=field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert validate_dependents_mock.call_count == 1 + validate_dependents_mock.assert_called_once_with( + True, {}, field_validation, empty_field_validation_logs + ) + + assert run_validators_mock.call_count == 1 + run_validators_mock.assert_called_once_with( + empty_field_validation_logs, field_validation, True, {} + ) + + assert validated_value == "run_validators_mock" + assert validated_metadata == {"async": True} + + +@pytest.mark.asyncio +async def test_async_validate_without_children(mocker): + validate_dependents_mock = mocker.patch.object(avs, "validate_dependents") + + run_validators_mock = mocker.patch.object(avs, "run_validators") + run_validators_mock.return_value = ("run_validators_mock", {"async": True}) + + validated_value, validated_metadata = await avs.async_validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert validate_dependents_mock.call_count == 0 + + assert run_validators_mock.call_count == 1 + run_validators_mock.assert_called_once_with( + empty_field_validation_logs, empty_field_validation, True, {} + ) + + assert validated_value == "run_validators_mock" + assert validated_metadata == {"async": True} + + +@pytest.mark.asyncio +async def test_validate_dependents(mocker): + async def mock_async_validate(v, md, *args): + return (f"new-{v}", md) + + async_validate_mock = mocker.patch.object( + avs, "async_validate", side_effect=mock_async_validate + ) + + gather_spy = mocker.spy(asyncio, "gather") + + child_one = FieldValidation( + key="child-one-key", value="child-one-value", validators=[], children=[] + ) + child_two = FieldValidation( + key="child-two-key", value="child-two-value", validators=[], children=[] + ) + field_validation = FieldValidation( + key="mock-parent-key", + value={"child-one-key": "child-one-value", "child-two-key": "child-two-value"}, + validators=[], + children=[child_one, child_two], + ) + + validated_value, validated_metadata = await avs.validate_dependents( + value=field_validation.value, + metadata={}, + validator_setup=field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert gather_spy.call_count == 1 + + assert async_validate_mock.call_count == 2 + async_validate_mock.assert_any_call( + child_one.value, {}, child_one, FieldValidationLogs() + ) + async_validate_mock.assert_any_call( + child_two.value, {}, child_two, FieldValidationLogs() + ) + + assert validated_value == { + "child-one-key": "new-child-one-value", + "child-two-key": "new-child-two-value", + } + assert validated_metadata == {} + + +@pytest.mark.asyncio +async def test_run_validators(mocker): + group_validators_mock = mocker.patch.object(avs, "group_validators") + fix_validator = MockValidator("fix_validator", "fix") + noop_validator_1 = MockValidator("noop_validator_1") + noop_validator_2 = MockValidator("noop_validator_2") + noop_validator_2.run_in_separate_process = True + group_validators_mock.return_value = [ + ("fix", [fix_validator]), + ("noop", [noop_validator_1, noop_validator_2]), + ] + + def mock_run_validator(validation_logs, validator, value, metadata): + return ValidatorLogs( + validator_name=validator.name, + value_before_validation=value, + validation_result=PassResult(), + ) + + run_validator_mock = mocker.patch.object( + avs, "run_validator", side_effect=mock_run_validator + ) + + mock_loop = MockLoop(True) + run_in_executor_spy = mocker.spy(mock_loop, "run_in_executor") + get_running_loop_mock = mocker.patch( + "asyncio.get_running_loop", return_value=mock_loop + ) + + async def mock_gather(*args): + return args + + asyancio_gather_mock = mocker.patch("asyncio.gather", side_effect=mock_gather) + + value, metadata = await avs.run_validators( + value=empty_field_validation.value, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert get_running_loop_mock.call_count == 1 + + assert group_validators_mock.call_count == 1 + group_validators_mock.assert_called_once_with(empty_field_validation.validators) + + assert run_in_executor_spy.call_count == 1 + run_in_executor_spy.assert_called_once_with( + avs.multiprocessing_executor, + run_validator_mock, + empty_field_validation_logs, + noop_validator_2, + empty_field_validation.value, + {}, + ) + + assert run_validator_mock.call_count == 3 + + assert asyancio_gather_mock.call_count == 1 + + assert value == empty_field_validation.value + assert metadata == {} + + +@pytest.mark.asyncio +async def test_run_validators_with_override(mocker): + group_validators_mock = mocker.patch.object(avs, "group_validators") + override_validator = MockValidator("override") + override_validator.override_value_on_pass = True + + group_validators_mock.return_value = [("exception", [override_validator])] + + run_validator_mock = mocker.patch.object(avs, "run_validator") + run_validator_mock.return_value = ValidatorLogs( + validator_name="override", + value_before_validation="mock-value", + validation_result=PassResult(value_override="override"), + ) + + mock_loop = MockLoop(True) + run_in_executor_spy = mocker.spy(mock_loop, "run_in_executor") + get_running_loop_mock = mocker.patch( + "asyncio.get_running_loop", return_value=mock_loop + ) + + asyancio_gather_mock = mocker.patch("asyncio.gather") + + value, metadata = await avs.run_validators( + value=empty_field_validation.value, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert get_running_loop_mock.call_count == 1 + + assert group_validators_mock.call_count == 1 + group_validators_mock.assert_called_once_with(empty_field_validation.validators) + + assert run_in_executor_spy.call_count == 0 + + assert run_validator_mock.call_count == 1 + + assert asyancio_gather_mock.call_count == 0 + + assert value == "override" + assert metadata == {} + + +# TODO +@pytest.mark.asyncio +async def test_run_validators_with_failures(mocker): + assert True is True diff --git a/tests/unit_tests/test_guard.py b/tests/unit_tests/test_guard.py new file mode 100644 index 000000000..7cfad793b --- /dev/null +++ b/tests/unit_tests/test_guard.py @@ -0,0 +1,143 @@ +import pytest +from pydantic import BaseModel + +import guardrails +from guardrails import Guard, Rail, Validator +from guardrails.datatypes import verify_metadata_requirements +from guardrails.validators import PassResult, register_validator + + +@register_validator("myrequiringvalidator", data_type="string") +class RequiringValidator(Validator): + required_metadata_keys = ["required_key"] + + def validate(self, value, metadata): + return PassResult() + + +@register_validator("myrequiringvalidator2", data_type="string") +class RequiringValidator2(Validator): + required_metadata_keys = ["required_key2"] + + def validate(self, value, metadata): + return PassResult() + + +@pytest.mark.parametrize( + "spec,metadata", + [ + ( + """ + + + + + + """, + {"required_key": "a"}, + ), + ( + """ + + + + + + + + + + + """, + {"required_key": "a", "required_key2": "b"}, + ), + ( + """ + + + + + + + + + + + + + + + + +""", + {"required_key": "a"}, + ), + ], +) +def test_required_metadata(spec, metadata): + guard = guardrails.Guard.from_rail_string(spec) + + with pytest.raises(ValueError): + guard.parse("{}") + missing_keys = verify_metadata_requirements( + {}, guard.output_schema.to_dict().values() + ) + assert set(missing_keys) == set(metadata) + + guard.parse("{}", metadata=metadata, num_reasks=0) + not_missing_keys = verify_metadata_requirements( + metadata, guard.output_schema.to_dict().values() + ) + assert not_missing_keys == [] + + +rail = Rail.from_string_validators([], "empty railspec") +empty_rail_string = """ + +""" + + +class EmptyModel(BaseModel): + empty_field: str + + +i_guard_none = Guard(rail) +i_guard_two = Guard(rail, 2) +r_guard_none = Guard.from_rail("tests/unit_tests/test_assets/empty.rail") +r_guard_two = Guard.from_rail("tests/unit_tests/test_assets/empty.rail", 2) +rs_guard_none = Guard.from_rail_string(empty_rail_string) +rs_guard_two = Guard.from_rail_string(empty_rail_string, 2) +py_guard_none = Guard.from_pydantic(output_class=EmptyModel) +py_guard_two = Guard.from_pydantic(output_class=EmptyModel, num_reasks=2) +s_guard_none = Guard.from_string(validators=[], description="empty railspec") +s_guard_two = Guard.from_string( + validators=[], description="empty railspec", num_reasks=2 +) + + +@pytest.mark.parametrize( + "guard,expected_num_reasks,config_num_reasks", + [ + (i_guard_none, 1, None), + (i_guard_two, 2, None), + (i_guard_none, 3, 3), + (r_guard_none, 1, None), + (r_guard_two, 2, None), + (r_guard_none, 3, 3), + (rs_guard_none, 1, None), + (rs_guard_two, 2, None), + (rs_guard_none, 3, 3), + (py_guard_none, 1, None), + (py_guard_two, 2, None), + (py_guard_none, 3, 3), + (s_guard_none, 1, None), + (s_guard_two, 2, None), + (s_guard_none, 3, 3), + ], +) +def test_configure(guard: Guard, expected_num_reasks: int, config_num_reasks: int): + guard.configure(config_num_reasks) + assert guard.num_reasks == expected_num_reasks diff --git a/tests/unit_tests/test_prompt.py b/tests/unit_tests/test_prompt.py index d384fbf7e..3d4f2c75f 100644 --- a/tests/unit_tests/test_prompt.py +++ b/tests/unit_tests/test_prompt.py @@ -1,5 +1,7 @@ """Unit tests for prompt and instructions parsing.""" +from string import Template + import pytest import guardrails as gd @@ -36,35 +38,35 @@ -{{user_instructions}} +${user_instructions} -{{user_prompt}} +${user_prompt} """ -RAIL_WITH_FORMAT_INSTRUCTIONS = f""" +RAIL_WITH_FORMAT_INSTRUCTIONS = """ -{INSTRUCTIONS} +You are a helpful bot, who answers only with valid JSON -{PROMPT} +Extract a string from the text -@complete_json_suffix_v2 +${gr.complete_json_suffix_v2} """ @@ -115,8 +117,8 @@ def test_format_instructions(): guard = gd.Guard.from_rail_string(RAIL_WITH_FORMAT_INSTRUCTIONS) output_schema = guard.rail.output_schema.transpile() expected_instructions = ( - constants["complete_json_suffix_v2"] - .format(output_schema=output_schema) + Template(constants["complete_json_suffix_v2"]) + .safe_substitute(output_schema=output_schema) .rstrip() ) @@ -127,7 +129,7 @@ def test_format_instructions(): "prompt_str,final_prompt", [ ( - "Dummy prompt. @complete_json_suffix_v2", + "Dummy prompt. ${gr.complete_json_suffix_v2}", f"Dummy prompt. {constants['complete_json_suffix_v2']}", ), ("Dummy prompt. some@email.com", "Dummy prompt. some@email.com"), diff --git a/tests/unit_tests/test_rail.py b/tests/unit_tests/test_rail.py index a2211272b..ceb8870f3 100644 --- a/tests/unit_tests/test_rail.py +++ b/tests/unit_tests/test_rail.py @@ -1,4 +1,10 @@ -from guardrails.rail import Rail +import warnings + +from lxml.etree import _Element, tostring +from pydantic import BaseModel, Field + +from guardrails.rail import Rail, generate_xml_code +from guardrails.validators import OneLine def test_rail_scalar_string(): @@ -147,3 +153,108 @@ def test_rail_list_with_object(): """ Rail.from_string(rail_spec) + + +def test_generate_xml_code_pydantic(): + class Joke(BaseModel): + joke: str = Field(validators=[OneLine()]) + + prompt = "Tell me a joke." + instructions = "Make sure it's funny." + reask_prompt = "That wasn't very funny. Tell me a different joke." + reask_instructions = "Make sure it's funny this time." + + xml: _Element = generate_xml_code( + prompt=prompt, + output_class=Joke, + instructions=instructions, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + ) + + actual_xml = tostring(xml, encoding="unicode", pretty_print=True) + + expected_xml = """ + + + + Tell me a joke. + Make sure it's funny. + That wasn't very funny. Tell me a different joke. + Make sure it's funny this time. + +""" + + assert actual_xml == expected_xml + + +def test_generate_xml_code_pydantic_with_validations_warning(mocker): + warn_spy = mocker.spy(warnings, "warn") + + class Joke(BaseModel): + joke: str = Field(validators=[OneLine()]) + + prompt = "Tell me a joke." + instructions = "Make sure it's funny." + reask_prompt = "That wasn't very funny. Tell me a different joke." + reask_instructions = "Make sure it's funny this time." + validations = [OneLine()] + + xml: _Element = generate_xml_code( + prompt=prompt, + output_class=Joke, + instructions=instructions, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + validators=validations, + ) + + actual_xml = tostring(xml, encoding="unicode", pretty_print=True) + + expected_xml = """ + + + + Tell me a joke. + Make sure it's funny. + That wasn't very funny. Tell me a different joke. + Make sure it's funny this time. + +""" + + assert actual_xml == expected_xml + + warn_spy.assert_called_once_with( + "Do not specify root level validators on a Pydantic model. These validators will be ignored." # noqa + ) + + +def test_generate_xml_code_string(): + prompt = "Tell me a joke." + instructions = "Make sure it's funny." + reask_prompt = "That wasn't very funny. Tell me a different joke." + reask_instructions = "Make sure it's funny this time." + validations = [OneLine()] + description = "Tell me a joke." + + xml: _Element = generate_xml_code( + prompt=prompt, + instructions=instructions, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + validators=validations, + description=description, + ) + + actual_xml = tostring(xml, encoding="unicode", pretty_print=True) + + expected_xml = """ + + Tell me a joke. + Make sure it's funny. + That wasn't very funny. Tell me a different joke. + Make sure it's funny this time. + +""" # noqa + + assert actual_xml == expected_xml diff --git a/tests/unit_tests/test_reask_utils.py b/tests/unit_tests/test_reask_utils.py new file mode 100644 index 000000000..e27a3ef0b --- /dev/null +++ b/tests/unit_tests/test_reask_utils.py @@ -0,0 +1,69 @@ +import pytest +from lxml.etree import Element, SubElement, tostring + +from guardrails.utils.reask_utils import ( + FieldReAsk, + gather_reasks, + get_pruned_tree, + prune_obj_for_reasking, +) +from guardrails.validators import FailResult, PydanticReAsk + +# FIXME: These tests are not exhaustive. +# They only add missing coverage from the 0.2 release +# We really should strive for close to 100% unit test coverage +# and use Integration tests for mimicking user flows + + +def test_gather_reasks(): + reask = PydanticReAsk() + reask["failed_prop"] = FieldReAsk( + path=["$.failed_prop.child"], + fail_results=[ + FailResult(error_message="child should not be None", outcome="fail") + ], + incorrect_value=None, + ) + + gathered_reasks = gather_reasks(reask) + + assert len(gathered_reasks) == 1 + assert gathered_reasks[0] == reask["failed_prop"] + + +empty_root = Element("root") +non_empty_root = Element("root") +property = SubElement(non_empty_root, "property") +property.attrib["format"] = "two-words" +child = SubElement(property, "child") +child.attrib["format"] = "two-words" +non_empty_output = Element("root") +output_property = SubElement(non_empty_output, "property") +output_child = SubElement(output_property, "child") +output_child.attrib["format"] = "two-words" + + +@pytest.mark.parametrize( + "root,reasks,expected_output", + [(empty_root, None, empty_root), (non_empty_root, [child], non_empty_output)], +) +def test_get_pruned_tree(root, reasks, expected_output): + actual_output = get_pruned_tree(root, reasks) + + assert tostring(actual_output) == tostring(expected_output) + + +def test_prune_obj_for_reasking(): + reask = FieldReAsk( + path=["$.failed_prop.child"], + fail_results=[ + FailResult(error_message="child should not be None", outcome="fail") + ], + incorrect_value=None, + ) + reasks = [reask, "not a reask"] + + pruned_reasks = prune_obj_for_reasking(reasks) + + assert len(pruned_reasks) == 1 + assert pruned_reasks[0] == reask diff --git a/tests/unit_tests/test_skeleton.py b/tests/unit_tests/test_skeleton.py index c3fbc3e6d..87e884694 100644 --- a/tests/unit_tests/test_skeleton.py +++ b/tests/unit_tests/test_skeleton.py @@ -80,7 +80,7 @@ ( """ - + - - - - - - - - """, - { - "action": "fight", - "fight": "punch", - }, - True, - ), - ( - """ - - - + - - - - - - """, { - "action": "fight", - "fight": "punch", - "flight": None, + "action": { + "action_type": "fight", + "fight_move": "punch", + } }, True, ), @@ -150,17 +115,16 @@ """ - - - - - - - - + + + + + + + - - + @@ -180,12 +143,13 @@ { "my_list3": [ { - "action": "fight", + "action_type": "fight", "fight": ["punch", "kick"], }, { - "action": "flight", - "flight": {"flight_direction": "north", "flight_speed": 1}, + "action_type": "flight", + "flight_direction": "north", + "flight_speed": 1, }, ], }, @@ -196,9 +160,10 @@ - + @@ -224,8 +189,10 @@ { "mychoices": { "some random thing": "string", - "action": "fight", - "fight": "punch", + "action": { + "action_type": "fight", + "fight_move": "punch", + }, }, }, True, diff --git a/tests/unit_tests/test_validator_service.py b/tests/unit_tests/test_validator_service.py new file mode 100644 index 000000000..f47cdfde4 --- /dev/null +++ b/tests/unit_tests/test_validator_service.py @@ -0,0 +1,80 @@ +import pytest + +import guardrails.validator_service as vs +from guardrails.datatypes import FieldValidation +from guardrails.utils.logs_utils import FieldValidationLogs + +from .mocks import MockAsyncValidatorService, MockLoop, MockSequentialValidatorService + +empty_field_validation = FieldValidation( + key="mock-key", value="mock-value", validators=[], children=[] +) +empty_field_validation_logs = FieldValidationLogs(validator_logs=[], children={}) + + +@pytest.mark.asyncio +async def test_async_validate(mocker): + mocker.patch( + "guardrails.validator_service.AsyncValidatorService", + new=MockAsyncValidatorService, + ) + validated_value, validated_metadata = await vs.async_validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert validated_value == "MockAsyncValidatorService.async_validate" + assert validated_metadata == {"async": True} + + +def test_validate_with_running_loop(mocker): + mockLoop = MockLoop(True) + mocker.patch( + "guardrails.validator_service.AsyncValidatorService", + new=MockAsyncValidatorService, + ) + mocker.patch( + "guardrails.validator_service.SequentialValidatorService", + new=MockSequentialValidatorService, + ) + mocker.patch("asyncio.get_event_loop", return_value=mockLoop) + + warn_spy = mocker.spy(vs.logger, "warning") + + validated_value, validated_metadata = vs.validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert warn_spy.call_count == 1 + warn_spy.assert_called_with( + "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead." # noqa + ) + assert validated_value == "MockSequentialValidatorService.validate" + assert validated_metadata == {"sync": True} + + +def test_validate_without_running_loop(mocker): + mockLoop = MockLoop(False) + mocker.patch( + "guardrails.validator_service.AsyncValidatorService", + new=MockAsyncValidatorService, + ) + mocker.patch( + "guardrails.validator_service.SequentialValidatorService", + new=MockSequentialValidatorService, + ) + mocker.patch("asyncio.get_event_loop", return_value=mockLoop) + validated_value, validated_metadata = vs.validate( + value=True, + metadata={}, + validator_setup=empty_field_validation, + validation_logs=empty_field_validation_logs, + ) + + assert validated_value == "MockAsyncValidatorService.validate" + assert validated_metadata == {"sync": True} diff --git a/tests/unit_tests/test_validators.py b/tests/unit_tests/test_validators.py index d49442cf6..55996edb3 100644 --- a/tests/unit_tests/test_validators.py +++ b/tests/unit_tests/test_validators.py @@ -1,16 +1,30 @@ +# noqa:W291 +from typing import Any, Dict + import pytest +from pydantic import BaseModel, Field +from guardrails import Guard +from guardrails.utils.reask_utils import FieldReAsk from guardrails.validators import ( BugFreeSQL, - EventDetail, + ExtractedSummarySentencesMatch, + ExtractiveSummary, + FailResult, Filter, + PassResult, Refrain, SimilarToDocument, SqlColumnPresence, + TwoWords, + ValidationResult, check_refrain_in_dict, filter_in_dict, + register_validator, ) +from .mock_embeddings import mock_create_embedding + @pytest.mark.parametrize( "input_dict, expected", @@ -61,8 +75,7 @@ def test_similar_to_document_validator(): ) summary = "All legislative powers are held by a Congress" " consisting of two chambers, the Senate and the House of Representatives." - schema = {"key": summary} - assert val.validate("key", summary, schema) == schema + assert isinstance(val.validate(summary, {}), PassResult) class TestBugFreeSQLValidator: @@ -74,13 +87,12 @@ def test_bug_free_sql(self): conn="sqlite://", ) bad_query = "select name, fro employees" - with pytest.raises(EventDetail) as context: - val.validate("sql-query", bad_query, {}) - assert context.type is EventDetail - assert context.value.error_message != "" + result = val.validate(bad_query, {}) + assert isinstance(result, FailResult) + assert result.error_message != "" good_query = "select name from employees;" - val.validate("sql-query", good_query, {}) + assert isinstance(val.validate(good_query, {}), PassResult) def test_long_sql_schema_no_exception(self): val = BugFreeSQL( @@ -92,22 +104,243 @@ def test_long_sql_schema_no_exception(self): def test_bug_free_sql_simple(self): val = BugFreeSQL() bad_query = "select name, fro employees" - with pytest.raises(EventDetail) as context: - val.validate("sql-query", bad_query, {}) - assert context.type is EventDetail - assert context.value.error_message != "" + + result = val.validate(bad_query, {}) + assert isinstance(result, FailResult) + assert result.error_message != "" good_query = "select name from employees;" - val.validate("sql-query", good_query, {}) + assert isinstance(val.validate(good_query, {}), PassResult) def test_sql_column_presense(self): sql = "select name, age from employees;" columns = ["name", "address"] val = SqlColumnPresence(cols=columns) - with pytest.raises(EventDetail) as context: - val.validate("sql-query", sql, {}) - assert context.type is EventDetail - assert context.value.error_message in ( + + result = val.validate(sql, {}) + assert isinstance(result, FailResult) + assert result.error_message in ( "Columns [age] not in [name, address]", "Columns [age] not in [address, name]", ) + + +def test_summary_validators(mocker): + pytest.importorskip("nltk", reason="nltk is not installed") + pytest.importorskip("thefuzz", reason="thefuzz is not installed") + + mocker.patch("openai.Embedding.create", new=mock_create_embedding) + mocker.patch("guardrails.embedding.OpenAIEmbedding.output_dim", new=2) + + summary = "It was a nice day. I went to the park. I saw a dog." + metadata = { + "filepaths": [ + "./tests/unit_tests/test_assets/article1.txt", + "./tests/unit_tests/test_assets/article2.txt", + ] + } + + val = ExtractedSummarySentencesMatch(threshold=0.1) + result = val.validate(summary, metadata) + assert isinstance(result, PassResult) + assert "citations" in result.metadata + assert "summary_with_citations" in result.metadata + assert result.metadata["citations"] == {1: 1, 2: 1, 3: 1} + assert ( + result.metadata["summary_with_citations"] + == """It was a nice day. [1] I went to the park. [1] I saw a dog. [1] + +[1] ./tests/unit_tests/test_assets/article1.txt +[2] ./tests/unit_tests/test_assets/article2.txt""" + ) + + val = ExtractiveSummary( + threshold=30, + ) + result = val.validate(summary, metadata) + assert isinstance(result, PassResult) + assert "citations" in result.metadata + assert "summary_with_citations" in result.metadata + assert result.metadata["citations"] == {1: 1, 2: 2, 3: 1} + assert ( + result.metadata["summary_with_citations"] + == """It was a nice day. [1] I went to the park. [2] I saw a dog. [1] + +[1] ./tests/unit_tests/test_assets/article1.txt +[2] ./tests/unit_tests/test_assets/article2.txt""" + ) + + +@register_validator("mycustomhellovalidator", data_type="string") +def hello_validator(value: Any, metadata: Dict[str, Any]) -> ValidationResult: + if "hello" in value.lower(): + return FailResult( + error_message="Hello is too basic, try something more creative.", + fix_value="hullo", + ) + return PassResult() + + +def test_validator_as_tuple(): + # (Callable, on_fail) tuple fix + class MyModel(BaseModel): + a_field: str = Field(..., validators=[(hello_validator, "fix")]) + + guard = Guard.from_pydantic(MyModel) + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hullo"} + + # (string, on_fail) tuple fix + + class MyModel(BaseModel): + a_field: str = Field( + ..., validators=[("two_words", "reask"), ("mycustomhellovalidator", "fix")] + ) + + guard = Guard.from_pydantic(MyModel) + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hullo"} + + # (Validator, on_fail) tuple fix + + class MyModel(BaseModel): + a_field: str = Field(..., validators=[(TwoWords(), "fix")]) + + guard = Guard.from_pydantic(MyModel) + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hello there"} + + # (Validator, on_fail) tuple reask + + hullo_reask = FieldReAsk( + incorrect_value="hello there yo", + fail_results=[ + FailResult( + error_message="Hello is too basic, try something more creative.", + fix_value="hullo", + ) + ], + path=["a_field"], + ) + + class MyModel(BaseModel): + a_field: str = Field(..., validators=[(hello_validator, "reask")]) + + guard = Guard.from_pydantic(MyModel) + + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hullo"} + assert ( + guard.guard_state.all_histories[0].history[0].parsed_output["a_field"] + == hullo_reask + ) + + hello_reask = FieldReAsk( + incorrect_value="hello there yo", + fail_results=[ + FailResult( + error_message="must be exactly two words", + fix_value="hello there", + ) + ], + path=["a_field"], + ) + + # (string, on_fail) tuple reask + + class MyModel(BaseModel): + a_field: str = Field(..., validators=[("two-words", "reask")]) + + guard = Guard.from_pydantic(MyModel) + + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hello there"} + assert ( + guard.guard_state.all_histories[0].history[0].parsed_output["a_field"] + == hello_reask + ) + + # (Validator, on_fail) tuple reask + + class MyModel(BaseModel): + a_field: str = Field(..., validators=[(TwoWords(), "reask")]) + + guard = Guard.from_pydantic(MyModel) + + output = guard.parse( + '{"a_field": "hello there yo"}', + num_reasks=0, + ) + + assert output == {"a_field": "hello there"} + assert ( + guard.guard_state.all_histories[0].history[0].parsed_output["a_field"] + == hello_reask + ) + + # Fail on string + + class MyModel(BaseModel): + a_field: str = Field(..., validators=["two-words"]) + + with pytest.raises(ValueError): + Guard.from_pydantic(MyModel) + + +def test_custom_func_validator(): + rail_str = """ + + + + + + """ + + guard = Guard.from_rail_string(rail_str) + + output = guard.parse( + '{"greeting": "hello"}', + num_reasks=0, + ) + assert output == {"greeting": "hullo"} + + guard_history = guard.guard_state.all_histories[0].history + assert len(guard_history) == 1 + validator_log = ( + guard_history[0].field_validation_logs.children["greeting"].validator_logs[0] + ) + assert validator_log.validator_name == "mycustomhellovalidator" + assert validator_log.validation_result == FailResult( + error_message="Hello is too basic, try something more creative.", + fix_value="hullo", + ) + + +def test_bad_validator(): + with pytest.raises(ValueError): + + @register_validator("mycustombadvalidator", data_type="string") + def validate(value: Any) -> ValidationResult: + pass diff --git a/tests/unit_tests/utils/test_docs_utils.py b/tests/unit_tests/utils/test_docs_utils.py new file mode 100644 index 000000000..6f03857be --- /dev/null +++ b/tests/unit_tests/utils/test_docs_utils.py @@ -0,0 +1,112 @@ +# from unittest.mock import Mock, patch + +import pytest + +from guardrails.utils.docs_utils import ( # sentence_split, + TextSplitter, + get_chunks_from_text, +) + + +class MockTokenizer: + def encode(self, text): + return [token_id for token_id in range(1, len(text) + 1)] + + def decode(self, tokens): + return " ".join([str(token_id) for token_id in tokens]) + + +class MockPromptTemplate: + def get_prompt_variables(self): + return ["var1", "var2"] + + def format(self, **kwargs): + return " ".join([f"{key}:{value}" for key, value in kwargs.items()]) + + +@pytest.fixture +def mock_tokenizer(monkeypatch): + mock = MockTokenizer() + monkeypatch.setattr("tiktoken.get_encoding", lambda _: mock) + return mock + + +@pytest.fixture +def mock_prompt_template(): + return MockPromptTemplate() + + +def test_text_splitter_split(mock_tokenizer): + text_splitter = TextSplitter() + text = "This is a test text." + chunks = text_splitter.split(text, tokens_per_chunk=10, token_overlap=5, buffer=2) + + assert len(chunks) == 7 + assert chunks[0] == "1 2 3 4 5 6 7 8" + assert chunks[1] == "4 5 6 7 8 9 10 11" + assert chunks[2] == "7 8 9 10 11 12 13 14" + assert chunks[3] == "10 11 12 13 14 15 16 17" + + +# @patch('nltk.data.find', side_effect=LookupError) +# def test_sentence_split_nltk_download_error(mock_nltk_find): +# with pytest.raises(ImportError): +# sentence_split("This is a test sentence.") + +# @patch('nltk.data.find') +# def test_sentence_split(mock_nltk_find): +# mock_nltk_find.return_value = True +# result = sentence_split("This is a test sentence.") +# assert len(result) == 1 +# assert result[0] == "This is a test sentence." + + +def test_prompt_template_token_length(mock_tokenizer, mock_prompt_template): + text_splitter = TextSplitter() + length = text_splitter.prompt_template_token_length(mock_prompt_template) + assert length == 11 # Assuming the encoded tokens count is 11 + + +def test_text_splitter_callable(mock_tokenizer): + text_splitter = TextSplitter() + text = "This is a test text." + chunks = text_splitter(text, tokens_per_chunk=10, token_overlap=5, buffer=2) + + assert len(chunks) == 7 + assert chunks[0] == "1 2 3 4 5 6 7 8" + assert chunks[1] == "4 5 6 7 8 9 10 11" + assert chunks[2] == "7 8 9 10 11 12 13 14" + assert chunks[3] == "10 11 12 13 14 15 16 17" + + +class MockNLTK: + @staticmethod + def sent_tokenize(text): + return ["sentence1", "sentence2", "sentence3"] + + @staticmethod + def word_tokenize(text): + return ["word1", "word2", "word3"] + + +def test_get_chunks_from_text_char(): + text = "This is a test." + chunks = get_chunks_from_text( + text, chunk_strategy="char", chunk_size=4, chunk_overlap=1 + ) + assert len(chunks) == 5 + assert chunks[0] == "T h i s" + assert chunks[1] == "s i s" + assert chunks[2] == "s a " + assert chunks[3] == " t e s" + assert chunks[4] == "s t ." + + +def test_get_chunks_from_text_invalid_strategy(): + with pytest.raises(ValueError): + get_chunks_from_text( + "Invalid strategy.", + chunk_strategy="invalid_strategy", + chunk_size=1, + chunk_overlap=0, + ) diff --git a/tests/unit_tests/utils/test_pydantic_utils.py b/tests/unit_tests/utils/test_pydantic_utils.py index c224795ea..4a38fe6fd 100644 --- a/tests/unit_tests/utils/test_pydantic_utils.py +++ b/tests/unit_tests/utils/test_pydantic_utils.py @@ -1,11 +1,16 @@ +from datetime import date, time +from typing import Union + import pytest -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, HttpUrl +from guardrails.datatypes import PythonCode from guardrails.utils.pydantic_utils import ( add_pydantic_validators_as_guardrails_validators, add_validator, + type_annotation_to_string, ) -from guardrails.validators import EventDetail, ValidChoices, ValidLength +from guardrails.validators import FailResult, PassResult, ValidChoices, ValidLength def test_add_pydantic_validators_as_guardrails_validators(): @@ -48,18 +53,17 @@ class DummyModel(BaseModel): assert isinstance( validators[0], ValidLength ), "First validator should be ValidLength" - validators[0].validate(None, "Beatrice", None) - with pytest.raises(EventDetail): - validators[0].validate(None, "MrAlexander", None) + assert isinstance(validators[0].validate("Beatrice", {}), PassResult) + assert isinstance(validators[0].validate("MrAlexander", {}), FailResult) # The second validator should be the ValidChoices validator assert isinstance( - validators[1], ValidChoices + validators[1][0], ValidChoices ), "Second validator should be ValidChoices" - validators[1].validate(None, "Alex", None) - validators[1].validate(None, "Bob", None) - with pytest.raises(EventDetail): - validators[1].validate(None, "Candace", None) + assert validators[1][1] == "reask" + assert isinstance(validators[1][0].validate("Alex", {}), PassResult) + assert isinstance(validators[1][0].validate("Bob", {}), PassResult) + assert isinstance(validators[1][0].validate("Candace", {}), FailResult) # TODO(shreya): Uncomment when custom validators are supported # # The third validator should be the dummy validator @@ -77,3 +81,36 @@ class DummyModel(BaseModel): # validators[3].validate(None, "Bob", None) # with pytest.raises(EventDetail): # validators[3].validate(None, "Alex", None) + + +@pytest.mark.parametrize( + "type_annotation,expected_type_string", + [ + (list, "list"), + (dict, "object"), + (bool, "bool"), + (date, "date"), + (float, "float"), + (int, "integer"), + (str, "string"), + (time, "time"), + (HttpUrl, "url"), + (Union[str, list], "choice"), + (PythonCode, "pythoncode"), + ], +) +def test_type_annotation_to_string(type_annotation, expected_type_string): + actual_type_string = type_annotation_to_string(type_annotation) + + assert actual_type_string == expected_type_string + + +def test_type_annotation_to_string_error(): + with pytest.raises(ValueError) as error: + + class UnsupportedType: + mock_property: str + + type_annotation_to_string(UnsupportedType) + + assert str(error) == f"Unsupported type: {UnsupportedType}" diff --git a/tests/unit_tests/utils/test_reask_utils.py b/tests/unit_tests/utils/test_reask_utils.py index a17eb9fb0..c60157b78 100644 --- a/tests/unit_tests/utils/test_reask_utils.py +++ b/tests/unit_tests/utils/test_reask_utils.py @@ -6,31 +6,107 @@ from guardrails import Instructions, Prompt from guardrails.schema import JsonSchema from guardrails.utils import reask_utils +from guardrails.utils.logs_utils import GuardLogs from guardrails.utils.reask_utils import ( FieldReAsk, gather_reasks, sub_reasks_with_fixed_values, ) +from guardrails.validators import FailResult @pytest.mark.parametrize( "input_dict, expected_dict", [ - ({"a": 1, "b": FieldReAsk(-1, "Error Msg", 1)}, {"a": 1, "b": 1}), ( - {"a": 1, "b": {"c": 2, "d": FieldReAsk(-1, "Error Msg", 2)}}, + { + "a": 1, + "b": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=1, + ) + ], + ), + }, + {"a": 1, "b": 1}, + ), + ( + { + "a": 1, + "b": { + "c": 2, + "d": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult(error_message="Error Msg", fix_value=2) + ], + ), + }, + }, {"a": 1, "b": {"c": 2, "d": 2}}, ), ( - {"a": [1, 2, FieldReAsk(-1, "Error Msg", 3)], "b": 4}, + { + "a": [ + 1, + 2, + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + ), + ], + "b": 4, + }, {"a": [1, 2, 3], "b": 4}, ), ( - {"a": [1, 2, {"c": FieldReAsk(-1, "Error Msg", 3)}]}, + { + "a": [ + 1, + 2, + { + "c": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + ) + }, + ], + }, {"a": [1, 2, {"c": 3}]}, ), ( - {"a": [1, 2, [3, 4, FieldReAsk(-1, "Error Msg", 5)]]}, + { + "a": [ + 1, + 2, + [ + 3, + 4, + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=5, + ) + ], + ), + ], + ] + }, {"a": [1, 2, [3, 4, 5]]}, ), ({"a": 1}, {"a": 1}), @@ -45,18 +121,123 @@ def test_gather_reasks(): """Test that reasks are gathered.""" input_dict = { "a": 1, - "b": FieldReAsk("b0", "Error Msg", "b1", None), - "c": {"d": FieldReAsk("c0", "Error Msg", "c1", "None")}, - "e": [1, 2, FieldReAsk("e0", "Error Msg", "e1", "None")], - "f": [1, 2, {"g": FieldReAsk("f0", "Error Msg", "f1", "None")}], - "h": [1, 2, [3, 4, FieldReAsk("h0", "Error Msg", "h1", "None")]], + "b": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=1, + ) + ], + ), + "c": { + "d": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=2, + ) + ], + ) + }, + "e": [ + 1, + 2, + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + ), + ], + "f": [ + 1, + 2, + { + "g": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=4, + ) + ], + ) + }, + ], + "h": [ + 1, + 2, + [ + 3, + 4, + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=5, + ) + ], + ), + ], + ], } expected_reasks = [ - FieldReAsk("b0", "Error Msg", "b1", ["b"]), - FieldReAsk("c0", "Error Msg", "c1", ["c", "d"]), - FieldReAsk("e0", "Error Msg", "e1", ["e", 2]), - FieldReAsk("f0", "Error Msg", "f1", ["f", 2, "g"]), - FieldReAsk("h0", "Error Msg", "h1", ["h", 2, 2]), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=1, + ) + ], + path=["b"], + ), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=2, + ) + ], + path=["c", "d"], + ), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + path=["e", 2], + ), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=4, + ) + ], + path=["f", 2, "g"], + ), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=5, + ) + ], + path=["h", 2, 2], + ), ] assert gather_reasks(input_dict) == expected_reasks @@ -65,28 +246,88 @@ def test_gather_reasks(): "input_dict, expected_dict", [ ( - {"a": 1, "b": FieldReAsk(-1, "Error Msg", 1)}, - {"b": FieldReAsk(-1, "Error Msg", 1)}, + { + "a": 1, + "b": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=1, + ) + ], + ), + }, + { + "b": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=1, + ) + ], + ) + }, ), ( - {"a": 1, "b": {"c": 2, "d": FieldReAsk(-1, "Error Msg", 2)}}, - {"b": {"d": FieldReAsk(-1, "Error Msg", 2)}}, + { + "a": 1, + "b": { + "c": 2, + "d": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=2, + ) + ], + ), + }, + }, + { + "b": { + "d": FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=2, + ) + ], + ) + } + }, ), ( - {"a": [1, 2, FieldReAsk(-1, "Error Msg", 3)], "b": 4}, { "a": [ - FieldReAsk(-1, "Error Msg", 3), - ] + 1, + 2, + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + ), + ], + "b": 4, }, - ), - ( - {"a": [1, 2, {"c": FieldReAsk(-1, "Error Msg", 3)}]}, { "a": [ - { - "c": FieldReAsk(-1, "Error Msg", 3), - } + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=3, + ) + ], + ), ] }, ), @@ -99,7 +340,7 @@ def test_prune_json_for_reasking(input_dict, expected_dict): @pytest.mark.parametrize( - "example_rail, reasks, reask_json", + "example_rail, reasks, original_response, reask_json", [ ( """ @@ -107,7 +348,21 @@ def test_prune_json_for_reasking(input_dict, expected_dict): """, - [reask_utils.FieldReAsk(-1, "Error Msg", "name", ["name"])], + [ + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value="name", + ) + ], + path=["name"], + ) + ], + { + "name": -1, + }, { "name": { "incorrect_value": -1, @@ -124,9 +379,31 @@ def test_prune_json_for_reasking(input_dict, expected_dict): """, [ - reask_utils.FieldReAsk(-1, "Error Msg", "name", ["name"]), - reask_utils.FieldReAsk(-1, "Error Msg", "age", ["age"]), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value="name", + ) + ], + path=["name"], + ), + FieldReAsk( + incorrect_value=-1, + fail_results=[ + FailResult( + error_message="Error Msg", + fix_value=5, + ) + ], + path=["age"], + ), ], + { + "name": -1, + "age": -1, + }, { "name": { "incorrect_value": -1, @@ -136,13 +413,13 @@ def test_prune_json_for_reasking(input_dict, expected_dict): "age": { "incorrect_value": -1, "error_message": "Error Msg", - "fix_value": "age", + "fix_value": 5, }, }, ), ], ) -def test_get_reask_prompt(example_rail, reasks, reask_json): +def test_get_reask_prompt(example_rail, reasks, original_response, reask_json): """Test that get_reask_prompt function returns the correct prompt.""" expected_result_template = """ I was given the following JSON response, which had problems due to incorrect values. @@ -162,22 +439,26 @@ def test_get_reask_prompt(example_rail, reasks, reask_json): ONLY return a valid JSON object (no other text is necessary), where the key of the field in JSON is the `name` attribute of the corresponding XML, and the value is of the type specified by the corresponding XML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. If you are unsure anywhere, enter `null`. Here are examples of simple (XML, JSON) pairs that show the expected behavior: -- `` => `{{'foo': 'example one'}}` -- `` => `{{"bar": ['STRING ONE', 'STRING TWO', etc.]}}` -- `` => `{{'baz': {{'foo': 'Some String', 'index': 1}}}}` +- `` => `{'foo': 'example one'}` +- `` => `{"bar": ['STRING ONE', 'STRING TWO', etc.]}` +- `` => `{'baz': {'foo': 'Some String', 'index': 1}}` """ # noqa: E501 output_schema = JsonSchema(ET.fromstring(example_rail)) + guard_logs = GuardLogs() + validated = output_schema.validate(guard_logs, original_response, {}) + reasks = output_schema.introspect(validated) ( reask_schema, result_prompt, instructions, - ) = output_schema.get_reask_setup(reasks, reask_json) + ) = output_schema.get_reask_setup(reasks, reask_json, False) assert result_prompt == Prompt( expected_result_template % ( - json.dumps(reask_json, indent=2).replace("{", "{{").replace("}", "}}"), + json.dumps(reask_json, indent=2), example_rail, ) ) + assert instructions == Instructions(expected_instructions)