diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45891ea37..881dda180 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: - main - dev - feat/* + - 0.*.* # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -19,7 +20,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -27,31 +28,27 @@ jobs: with: python-version: ${{ matrix.python-version }} - # - name: Poetry cache - # uses: actions/cache@v3 - # with: - # path: ~/.cache/pypoetry - # key: poetry-cache-${{ runner.os }}-${{ matrix.python-version }}-${{ env.POETRY_VERSION }} - - name: Install Poetry uses: snok/install-poetry@v1 - name: Install Dependencies # TODO: fix errors so that we can run `make dev` instead run: | + # Setup Virtual Environment + python3 -m venv ./.venv + source .venv/bin/activate make full - name: Lint with ruff run: | + source .venv/bin/activate make lint Typing: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] - pydantic-version: ["==1.10.9", ">=2.x"] - openai-version: [">=1.x"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -59,66 +56,67 @@ jobs: with: python-version: ${{ matrix.python-version }} - # - name: Poetry cache - # uses: actions/cache@v3 - # with: - # path: ~/.cache/pypoetry - # key: poetry-cache-${{ runner.os }}-${{ matrix.python-version }}-${{ env.POETRY_VERSION }}-${{ matrix.pydantic-version }} - - name: Install Poetry uses: snok/install-poetry@v1 - name: Install Dependencies # TODO: fix errors so that we can run `make dev` instead run: | + # Setup Virtual Environment + python3 -m venv ./.venv + source .venv/bin/activate make full - poetry run pip install "pydantic${{ matrix.pydantic-version }}" - poetry run pip install "openai${{ matrix.openai-version }}" - name: Static analysis with pyright run: | + source .venv/bin/activate make type Pytests: runs-on: LargeBois strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] # TODO: fix errors so that we can run both `make dev` and `make full` # dependencies: ['dev', 'full'] - dependencies: ["full"] - pydantic-version: ["==1.10.9", ">=2.x"] - openai-version: [">=1.x"] + # dependencies: ["full"] steps: - uses: actions/checkout@v4 - name: Create .guardrailsrc run: | echo 'id="SYSTEM TESTING"' > ~/.guardrailsrc - echo 'no_metrics=false' >> ~/.guardrailsrc + echo 'enable_metrics=false' >> ~/.guardrailsrc - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - # - name: Poetry cache - # uses: actions/cache@v3 - # with: - # path: ~/.cache/pypoetry - # key: poetry-cache-${{ runner.os }}-${{ matrix.python-version }}-${{ env.POETRY_VERSION }}-${{ matrix.pydantic-version }}-${{ matrix.openai-version }} - - name: Install Poetry uses: snok/install-poetry@v1 - name: Install Dependencies run: | - make ${{ matrix.dependencies }} - poetry run pip install "pydantic${{ matrix.pydantic-version }}" - poetry run pip install "openai${{ matrix.openai-version }}" + # Setup Virtual Environment + python3 -m venv ./.venv + source .venv/bin/activate + + make full + if [ "${{ matrix.python-version }}" == "3.12" ]; then + echo "Installing latest langchain-core and langsmith from PyPI" + pip install "langchain-core>=0.2" "langsmith<0.2.0,>=0.1.75" + fi - name: Run Pytests run: | + source .venv/bin/activate + + echo "langchain-core version:" + pip show langchain-core + echo "langsmith version:" + pip show langsmith + make test-cov - name: Upload to codecov.io diff --git a/Makefile b/Makefile index 59f3d0fc5..6fb50b3e2 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,5 @@ MKDOCS_SERVE_ADDR ?= localhost:8000 # Default address for mkdocs serve, format: :, override with `make docs-serve MKDOCS_SERVE_ADDR=:` -# Extract major package versions for OpenAI and Pydantic -OPENAI_VERSION_MAJOR := $(shell poetry run python -c 'import openai; print(openai.__version__.split(".")[0])') -PYDANTIC_VERSION_MAJOR := $(shell poetry run python -c 'import pydantic; print(pydantic.__version__.split(".")[0])') - -# Construct the typing command using only major versions -TYPING_CMD := type-pydantic-v$(PYDANTIC_VERSION_MAJOR)-openai-v$(OPENAI_VERSION_MAJOR) - autoformat: poetry run ruff check guardrails/ tests/ --fix poetry run ruff format guardrails/ tests/ @@ -14,27 +7,7 @@ autoformat: .PHONY: type type: - @make $(TYPING_CMD) - -type-pydantic-v1-openai-v0: - echo '{"reportDeprecated": true, "exclude": ["guardrails/utils/pydantic_utils/v2.py", "guardrails/utils/openai_utils/v1.py"]}' > pyrightconfig.json - poetry run pyright guardrails/ - rm pyrightconfig.json - -type-pydantic-v1-openai-v1: - echo '{"reportDeprecated": true, "exclude": ["guardrails/utils/pydantic_utils/v2.py", "guardrails/utils/openai_utils/v0.py"]}' > pyrightconfig.json - poetry run pyright guardrails/ - rm pyrightconfig.json - -type-pydantic-v2-openai-v0: - echo '{"reportDeprecated": true, "exclude": ["guardrails/utils/pydantic_utils/v1.py", "guardrails/utils/openai_utils/v1.py"]}' > pyrightconfig.json - poetry run pyright guardrails/ - rm pyrightconfig.json - -type-pydantic-v2-openai-v1: - echo '{"reportDeprecated": true, "exclude": ["guardrails/utils/pydantic_utils/v1.py", "guardrails/utils/openai_utils/v0.py"]}' > pyrightconfig.json poetry run pyright guardrails/ - rm pyrightconfig.json lint: poetry run ruff check guardrails/ tests/ @@ -85,3 +58,15 @@ precommit: pyright guardrails/ make lint ./.github/workflows/scripts/update_notebook_matrix.sh + +refresh: + echo "Removing old virtual environment" + rm -rf ./.venv; + echo "Creating new virtual environment" + python3 -m venv ./.venv; + echo "Sourcing and installing" + source ./.venv/bin/activate && make full; + +update-lock: + poetry lock --no-update + diff --git a/docs/api_reference/data_types.md b/docs/api_reference/data_types.md index cceadf2c6..1cc62daec 100644 --- a/docs/api_reference/data_types.md +++ b/docs/api_reference/data_types.md @@ -3,16 +3,5 @@ ::: guardrails.datatypes options: filters: - - "!get_validators" - - "!registry" - - "!DataType" - - "!register_type" - - "!Scalar" - - "!set_children" - - "!validate" - - "!from_str" - - "!from_xml" - - "!model" - - "!validators" - - "!to_object_element" + - "!types_registry" show_bases: true diff --git a/docs/api_reference/guard.md b/docs/api_reference/guard.md index 854732d21..1c4950e64 100644 --- a/docs/api_reference/guard.md +++ b/docs/api_reference/guard.md @@ -4,11 +4,17 @@ ::: guardrails.guard.Guard options: members: + - "__init__" - "from_rail" - "from_rail_string" - "from_pydantic" - "from_string" + - "use" + - "use_many" + - "to_runnable" - "configure" - "__call__" - "parse" - - "state" + - "validate" + - "history" + - "error_spans_in_output" diff --git a/docs/api_reference/rail.md b/docs/api_reference/rail.md deleted file mode 100644 index cab956f4b..000000000 --- a/docs/api_reference/rail.md +++ /dev/null @@ -1,13 +0,0 @@ -::: 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/schema.md b/docs/api_reference/schema.md deleted file mode 100644 index f92f07b90..000000000 --- a/docs/api_reference/schema.md +++ /dev/null @@ -1 +0,0 @@ -::: guardrails.schema \ 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 d377b4eb5..fe3bde54d 100644 --- a/docs/examples/bug_free_python_code.ipynb +++ b/docs/examples/bug_free_python_code.ipynb @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -50,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -63,7 +63,7 @@ "Problem Description:\n", "${leetcode_problem}\n", "\n", - "${gr.complete_json_suffix}\"\"\"\n", + "${gr.complete_xml_suffix}\"\"\"\n", "\n", "class BugFreePythonCode(BaseModel):\n", " python_code: str = Field(validators=[ValidPython(on_fail=\"reask\")])\n", @@ -88,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -106,11 +106,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=BugFreePythonCode, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=BugFreePythonCode)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wrap the LLM API call with `Guard`" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "\n", + "leetcode_problem = \"\"\"\n", + "Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.\n", + "\"\"\"\n", + "\n", + "response = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={\"leetcode_problem\": leetcode_problem},\n", + " max_tokens=2048,\n", + " temperature=0,\n", + ")" ] }, { @@ -123,7 +152,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -134,16 +163,17 @@ "problem.\n", "\n", "Problem Description:\n", - "${leetcode_problem}\n", + "\n", + "Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.\n", + "\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=\"python_code\" format=\"reflex/valid_python\"/>\n", + " <string format=\"reflex/valid_python\" name=\"python_code\" required=\"true\"></string>\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", @@ -163,16 +193,17 @@ "problem.\n", "\n", "Problem Description:\n", - "$\u001b[1m{\u001b[0mleetcode_problem\u001b[1m}\u001b[0m\n", + "\n", + "Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is \u001b[1;36m1000\u001b[0m.\n", + "\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\u001b[35m/\u001b[0m\u001b[95mstring\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[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", @@ -191,35 +222,7 @@ } ], "source": [ - "print(guard.rail.prompt)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "\n", - "leetcode_problem = \"\"\"\n", - "Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.\n", - "\"\"\"\n", - "\n", - "response = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={\"leetcode_problem\": leetcode_problem},\n", - " max_tokens=2048,\n", - " temperature=0,\n", - ")" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -234,30 +237,32 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
{\n",
-       "    'python_code': 'def longest_palindromic_substring(s):\\n    if not s:\\n        return \"\"\\n    start = 0\\n    end\n",
-       "= 0\\n    for i in range(len(s)):\\n        len1 = expand_around_center(s, i, i)\\n        len2 = \n",
-       "expand_around_center(s, i, i + 1)\\n        max_len = max(len1, len2)\\n        if max_len > end - start:\\n          \n",
-       "start = i - (max_len - 1) // 2\\n            end = i + max_len // 2\\n    return s[start:end + 1]\\n\\n\\ndef \n",
-       "expand_around_center(s, left, right):\\n    while left >= 0 and right < len(s) and s[left] == s[right]:\\n        \n",
-       "left -= 1\\n        right += 1\\n    return right - left - 1'\n",
+       "    'python_code': 'class Solution:\\n    def longestPalindrome(self, s: str) -> str:\\n        if len(s) == 0:\\n    \n",
+       "return \"\"\\n        start = 0\\n        end = 0\\n        for i in range(len(s)):\\n            len1 = \n",
+       "self.expandAroundCenter(s, i, i)\\n            len2 = self.expandAroundCenter(s, i, i + 1)\\n            max_len = \n",
+       "max(len1, len2)\\n            if max_len > end - start:\\n                start = i - (max_len - 1) // 2\\n           \n",
+       "end = i + max_len // 2\\n        return s[start:end + 1]\\n\\n    def expandAroundCenter(self, s: str, left: int, \n",
+       "right: int) -> int:\\n        while left >= 0 and right < len(s) and s[left] == s[right]:\\n            left -= 1\\n  \n",
+       "right += 1\\n        return right - left - 1'\n",
        "}\n",
        "
\n" ], "text/plain": [ "\u001b[1m{\u001b[0m\n", - " \u001b[32m'python_code'\u001b[0m: \u001b[32m'def longest_palindromic_substring\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m:\\n if not s:\\n return \"\"\\n start = 0\\n end\u001b[0m\n", - "\u001b[32m= 0\\n for i in range\u001b[0m\u001b[32m(\u001b[0m\u001b[32mlen\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m)\u001b[0m\u001b[32m:\\n len1 = expand_around_center\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms, i, i\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n len2 = \u001b[0m\n", - "\u001b[32mexpand_around_center\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms, i, i + 1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n max_len = max\u001b[0m\u001b[32m(\u001b[0m\u001b[32mlen1, len2\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n if max_len > end - start:\\n \u001b[0m\n", - "\u001b[32mstart = i - \u001b[0m\u001b[32m(\u001b[0m\u001b[32mmax_len - 1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m // 2\\n end = i + max_len // 2\\n return s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mstart:end + 1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\n\\n\\ndef \u001b[0m\n", - "\u001b[32mexpand_around_center\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms, left, right\u001b[0m\u001b[32m)\u001b[0m\u001b[32m:\\n while left >= 0 and right < len\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m and s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mleft\u001b[0m\u001b[32m]\u001b[0m\u001b[32m == s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mright\u001b[0m\u001b[32m]\u001b[0m\u001b[32m:\\n \u001b[0m\n", - "\u001b[32mleft -= 1\\n right += 1\\n return right - left - 1'\u001b[0m\n", + " \u001b[32m'python_code'\u001b[0m: \u001b[32m'class Solution:\\n def longestPalindrome\u001b[0m\u001b[32m(\u001b[0m\u001b[32mself, s: str\u001b[0m\u001b[32m)\u001b[0m\u001b[32m -> str:\\n if len\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m == 0:\\n \u001b[0m\n", + "\u001b[32mreturn \"\"\\n start = 0\\n end = 0\\n for i in range\u001b[0m\u001b[32m(\u001b[0m\u001b[32mlen\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m)\u001b[0m\u001b[32m:\\n len1 = \u001b[0m\n", + "\u001b[32mself.expandAroundCenter\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms, i, i\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n len2 = self.expandAroundCenter\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms, i, i + 1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n max_len = \u001b[0m\n", + "\u001b[32mmax\u001b[0m\u001b[32m(\u001b[0m\u001b[32mlen1, len2\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n if max_len > end - start:\\n start = i - \u001b[0m\u001b[32m(\u001b[0m\u001b[32mmax_len - 1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m // 2\\n \u001b[0m\n", + "\u001b[32mend = i + max_len // 2\\n return s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mstart:end + 1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\n\\n def expandAroundCenter\u001b[0m\u001b[32m(\u001b[0m\u001b[32mself, s: str, left: int, \u001b[0m\n", + "\u001b[32mright: int\u001b[0m\u001b[32m)\u001b[0m\u001b[32m -> int:\\n while left >= 0 and right < len\u001b[0m\u001b[32m(\u001b[0m\u001b[32ms\u001b[0m\u001b[32m)\u001b[0m\u001b[32m and s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mleft\u001b[0m\u001b[32m]\u001b[0m\u001b[32m == s\u001b[0m\u001b[32m[\u001b[0m\u001b[32mright\u001b[0m\u001b[32m]\u001b[0m\u001b[32m:\\n left -= 1\\n \u001b[0m\n", + "\u001b[32mright += 1\\n return right - left - 1'\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] }, @@ -279,55 +284,55 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
def longest_palindromic_substring(s):\n",
-       "    if not s:\n",
-       "        return \"\"\n",
-       "    start = 0\n",
-       "    end = 0\n",
-       "    for i in range(len(s)):\n",
-       "        len1 = expand_around_center(s, i, i)\n",
-       "        len2 = expand_around_center(s, i, i + 1)\n",
-       "        max_len = max(len1, len2)\n",
-       "        if max_len > end - start:\n",
-       "            start = i - (max_len - 1) // 2\n",
-       "            end = i + max_len // 2\n",
-       "    return s\n",
-       "\n",
+       "
class Solution:\n",
+       "    def longestPalindrome(self, s: str) -> str:\n",
+       "        if len(s) == 0:\n",
+       "            return \"\"\n",
+       "        start = 0\n",
+       "        end = 0\n",
+       "        for i in range(len(s)):\n",
+       "            len1 = self.expandAroundCenter(s, i, i)\n",
+       "            len2 = self.expandAroundCenter(s, i, i + 1)\n",
+       "            max_len = max(len1, len2)\n",
+       "            if max_len > end - start:\n",
+       "                start = i - (max_len - 1) // 2\n",
+       "                end = i + max_len // 2\n",
+       "        return s\n",
        "\n",
-       "def expand_around_center(s, left, right):\n",
-       "    while left >= 0 and right < len(s) and s == s:\n",
-       "        left -= 1\n",
-       "        right += 1\n",
-       "    return right - left - 1\n",
+       "    def expandAroundCenter(self, s: str, left: int, right: int) -> int:\n",
+       "        while left >= 0 and right < len(s) and s == s:\n",
+       "            left -= 1\n",
+       "            right += 1\n",
+       "        return right - left - 1\n",
        "
\n" ], "text/plain": [ - "def \u001b[1;35mlongest_palindromic_substring\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m:\n", - " if not s:\n", - " return \u001b[32m\"\"\u001b[0m\n", - " start = \u001b[1;36m0\u001b[0m\n", - " end = \u001b[1;36m0\u001b[0m\n", - " for i in \u001b[1;35mrange\u001b[0m\u001b[1m(\u001b[0m\u001b[1;35mlen\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m\u001b[1m)\u001b[0m:\n", - " len1 = \u001b[1;35mexpand_around_center\u001b[0m\u001b[1m(\u001b[0ms, i, i\u001b[1m)\u001b[0m\n", - " len2 = \u001b[1;35mexpand_around_center\u001b[0m\u001b[1m(\u001b[0ms, i, i + \u001b[1;36m1\u001b[0m\u001b[1m)\u001b[0m\n", - " max_len = \u001b[1;35mmax\u001b[0m\u001b[1m(\u001b[0mlen1, len2\u001b[1m)\u001b[0m\n", - " if max_len > end - start:\n", - " start = i - \u001b[1m(\u001b[0mmax_len - \u001b[1;36m1\u001b[0m\u001b[1m)\u001b[0m \u001b[35m/\u001b[0m\u001b[35m/\u001b[0m \u001b[1;36m2\u001b[0m\n", - " end = i + max_len \u001b[35m/\u001b[0m\u001b[35m/\u001b[0m \u001b[1;36m2\u001b[0m\n", - " return s\n", + "class Solution:\n", + " def \u001b[1;35mlongestPalindrome\u001b[0m\u001b[1m(\u001b[0mself, s: str\u001b[1m)\u001b[0m -> str:\n", + " if \u001b[1;35mlen\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m == \u001b[1;36m0\u001b[0m:\n", + " return \u001b[32m\"\"\u001b[0m\n", + " start = \u001b[1;36m0\u001b[0m\n", + " end = \u001b[1;36m0\u001b[0m\n", + " for i in \u001b[1;35mrange\u001b[0m\u001b[1m(\u001b[0m\u001b[1;35mlen\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m\u001b[1m)\u001b[0m:\n", + " len1 = \u001b[1;35mself.expandAroundCenter\u001b[0m\u001b[1m(\u001b[0ms, i, i\u001b[1m)\u001b[0m\n", + " len2 = \u001b[1;35mself.expandAroundCenter\u001b[0m\u001b[1m(\u001b[0ms, i, i + \u001b[1;36m1\u001b[0m\u001b[1m)\u001b[0m\n", + " max_len = \u001b[1;35mmax\u001b[0m\u001b[1m(\u001b[0mlen1, len2\u001b[1m)\u001b[0m\n", + " if max_len > end - start:\n", + " start = i - \u001b[1m(\u001b[0mmax_len - \u001b[1;36m1\u001b[0m\u001b[1m)\u001b[0m \u001b[35m/\u001b[0m\u001b[35m/\u001b[0m \u001b[1;36m2\u001b[0m\n", + " end = i + max_len \u001b[35m/\u001b[0m\u001b[35m/\u001b[0m \u001b[1;36m2\u001b[0m\n", + " return s\n", "\n", - "\n", - "def \u001b[1;35mexpand_around_center\u001b[0m\u001b[1m(\u001b[0ms, left, right\u001b[1m)\u001b[0m:\n", - " while left >= \u001b[1;36m0\u001b[0m and right < \u001b[1;35mlen\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m and s == s:\n", - " left -= \u001b[1;36m1\u001b[0m\n", - " right += \u001b[1;36m1\u001b[0m\n", - " return right - left - \u001b[1;36m1\u001b[0m\n" + " def \u001b[1;35mexpandAroundCenter\u001b[0m\u001b[1m(\u001b[0mself, s: str, left: int, right: int\u001b[1m)\u001b[0m -> int:\n", + " while left >= \u001b[1;36m0\u001b[0m and right < \u001b[1;35mlen\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m and s == s:\n", + " left -= \u001b[1;36m1\u001b[0m\n", + " right += \u001b[1;36m1\u001b[0m\n", + " return right - left - \u001b[1;36m1\u001b[0m\n" ] }, "metadata": {}, @@ -351,7 +356,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -393,7 +398,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/check_for_pii.ipynb b/docs/examples/check_for_pii.ipynb index 5afe56212..e3fd94814 100644 --- a/docs/examples/check_for_pii.ipynb +++ b/docs/examples/check_for_pii.ipynb @@ -14,7 +14,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -52,7 +52,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -115,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -176,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -226,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -292,7 +292,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/competitors_check.ipynb b/docs/examples/competitors_check.ipynb index 8f9278c0b..7bf5d1d89 100644 --- a/docs/examples/competitors_check.ipynb +++ b/docs/examples/competitors_check.ipynb @@ -19,18 +19,18 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: nltk in ./.venv/lib/python3.10/site-packages (3.8.1)\n", - "Requirement already satisfied: click in ./.venv/lib/python3.10/site-packages (from nltk) (8.1.7)\n", - "Requirement already satisfied: joblib in ./.venv/lib/python3.10/site-packages (from nltk) (1.4.2)\n", - "Requirement already satisfied: regex>=2021.8.3 in ./.venv/lib/python3.10/site-packages (from nltk) (2023.12.25)\n", - "Requirement already satisfied: tqdm in ./.venv/lib/python3.10/site-packages (from nltk) (4.66.4)\n", + "Requirement already satisfied: nltk in ./.venv/lib/python3.12/site-packages (3.8.1)\n", + "Requirement already satisfied: click in ./.venv/lib/python3.12/site-packages (from nltk) (8.1.7)\n", + "Requirement already satisfied: joblib in ./.venv/lib/python3.12/site-packages (from nltk) (1.4.2)\n", + "Requirement already satisfied: regex>=2021.8.3 in ./.venv/lib/python3.12/site-packages (from nltk) (2023.12.25)\n", + "Requirement already satisfied: tqdm in ./.venv/lib/python3.12/site-packages (from nltk) (4.66.4)\n", "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mcompetitor_check...\u001b[0m\n", "✅Successfully installed guardrails/competitor_check!\n", "\n", @@ -39,32 +39,15 @@ } ], "source": [ - "! pip install nltk\n", + "! pip install nltk --quiet\n", "! guardrails hub install hub://guardrails/competitor_check --quiet" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 8, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validators/__init__.py:51: FutureWarning: \n", - " Importing validators from `guardrails.validators` is deprecated.\n", - " All validators are now available in the Guardrails Hub. Please install\n", - " and import them from the hub instead. All validators will be\n", - " removed from this module in the next major release.\n", - "\n", - " Install with: `guardrails hub install hub:///`\n", - " Import as: from guardrails.hub import `ValidatorName`\n", - " \n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "from guardrails.hub import CompetitorCheck\n", "import guardrails as gd\n", @@ -89,7 +72,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -123,7 +106,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -158,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -230,7 +213,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -325,7 +308,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/extracting_entities.ipynb b/docs/examples/extracting_entities.ipynb index 901132224..d9be0c291 100644 --- a/docs/examples/extracting_entities.ipynb +++ b/docs/examples/extracting_entities.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -29,7 +29,11 @@ "✅Successfully installed guardrails/one_line!\n", "\n", "\n", - "Requirement already satisfied: pypdfium2 in ./.venv/lib/python3.10/site-packages (4.30.0)\n", + "Collecting pypdfium2\n", + " Using cached pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl.metadata (48 kB)\n", + "Using cached pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl (2.7 MB)\n", + "Installing collected packages: pypdfium2\n", + "Successfully installed pypdfium2-4.30.0\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } @@ -69,17 +73,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/pypdfium2/_helpers/textpage.py:80: UserWarning: get_text_range() call with default params will be implicitly redirected to get_text_bounded()\n", - " warnings.warn(\"get_text_range() call with default params will be implicitly redirected to get_text_bounded()\")\n" - ] - }, { "data": { "text/html": [ @@ -136,7 +132,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -148,7 +144,7 @@ "\n", "${document}\n", "\n", - "${gr.complete_json_suffix_v2}\n", + "${gr.complete_xml_suffix_v2}\n", "\"\"\"\n", "\n", "class Fee(BaseModel):\n", @@ -181,11 +177,11 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement)" ] }, { @@ -208,7 +204,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -216,6 +212,7 @@ "\n", "raw_llm_response, validated_response, *rest = guard(\n", " openai.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\"document\": content[:6000]},\n", " model=\"gpt-3.5-turbo-instruct\",\n", " max_tokens=2048,\n", @@ -235,7 +232,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -243,7 +240,7 @@ "text/html": [ "
{\n",
        "    'fees': [\n",
-       "        {'name': 'annual membership', 'explanation': 'None', 'value': 0.0},\n",
+       "        {'name': 'annual membership', 'explanation': 'None', 'value': 0},\n",
        "        {\n",
        "            'name': 'my chase',\n",
        "            'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction or amount \n",
@@ -251,9 +248,9 @@
        "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': 0.0\n",
+       "            'value': 0\n",
        "        },\n",
-       "        {'name': 'transaction fees', 'explanation': 'None', 'value': 0.0},\n",
+       "        {'name': 'transaction fees', 'explanation': 'None', 'value': 0},\n",
        "        {\n",
        "            'name': 'balance transfers',\n",
        "            'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater, on transfers made\n",
@@ -271,11 +268,11 @@
        "            'explanation': '3% of the amount of each transaction in U.S. dollars.',\n",
        "            'value': 3.0\n",
        "        },\n",
-       "        {'name': 'penalty fees', 'explanation': 'None', 'value': 0.0},\n",
+       "        {'name': 'penalty fees', 'explanation': 'None', 'value': 0},\n",
        "        {'name': 'late payment', 'explanation': 'Up to $40.', 'value': 40.0},\n",
-       "        {'name': 'over the', 'explanation': 'None', 'value': 0.0},\n",
+       "        {'name': 'over the', 'explanation': 'None', 'value': 0},\n",
        "        {'name': 'return payment', 'explanation': 'Up to $40.', 'value': 40.0},\n",
-       "        {'name': 'return check', 'explanation': 'None', 'value': 0.0}\n",
+       "        {'name': 'return check', 'explanation': 'None', 'value': 0}\n",
        "    ],\n",
        "    'interest_rates': [\n",
        "        {'account_type': 'purchase/my chase loan/balance transfer', 'rate': 19.49},\n",
@@ -288,7 +285,7 @@
       "text/plain": [
        "\u001b[1m{\u001b[0m\n",
        "    \u001b[32m'fees'\u001b[0m: \u001b[1m[\u001b[0m\n",
-       "        \u001b[1m{\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\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\u001b[0m\u001b[1m}\u001b[0m,\n",
        "        \u001b[1m{\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",
@@ -296,9 +293,9 @@
        "\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;36m0.0\u001b[0m\n",
+       "            \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\n",
        "        \u001b[1m}\u001b[0m,\n",
-       "        \u001b[1m{\u001b[0m\u001b[32m'name'\u001b[0m: \u001b[32m'transaction fees'\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\u001b[32m'name'\u001b[0m: \u001b[32m'transaction fees'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'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'name'\u001b[0m: \u001b[32m'balance transfers'\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 made\u001b[0m\n",
@@ -316,11 +313,11 @@
        "            \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'name'\u001b[0m: \u001b[32m'penalty fees'\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\u001b[32m'name'\u001b[0m: \u001b[32m'penalty fees'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n",
        "        \u001b[1m{\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'name'\u001b[0m: \u001b[32m'over the'\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\u001b[32m'name'\u001b[0m: \u001b[32m'over the'\u001b[0m, \u001b[32m'explanation'\u001b[0m: \u001b[32m'None'\u001b[0m, \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n",
        "        \u001b[1m{\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'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[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m\n",
+       "        \u001b[1m{\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[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[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'purchase/my chase loan/balance transfer'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n",
@@ -340,7 +337,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 136,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
@@ -481,23 +478,25 @@
        "│   │ │ it into.                                                                                                │ │\n",
        "│   │ │                                                                                                         │ │\n",
        "│   │ │ <output>                                                                                                │ │\n",
-       "│   │ │     <list name=\"fees\" description=\"What fees and charges are associated with my account?\">              │ │\n",
-       "│   │ │         <object>                                                                                        │ │\n",
-       "│   │ │             <string name=\"name\" format=\"guardrails/lowercase; guardrails/two_words\"/>                   │ │\n",
-       "│   │ │             <string name=\"explanation\" format=\"guardrails/one_line\"/>                                   │ │\n",
-       "│   │ │             <float name=\"value\" description=\"The fee amount in USD or as a percentage.\"/>               │ │\n",
-       "│   │ │         </object>                                                                                       │ │\n",
-       "│   │ │     </list>                                                                                             │ │\n",
-       "│   │ │     <list name=\"interest_rates\" description=\"What are the interest rates offered by the bank on         │ │\n",
-       "│   │ │ different kinds of accounts and products?\">                                                             │ │\n",
-       "│   │ │         <object>                                                                                        │ │\n",
-       "│   │ │             <string name=\"account_type\" format=\"guardrails/lowercase\"/>                                 │ │\n",
-       "│   │ │             <float name=\"rate\" description=\"The annual percentage rate (APR) for the account type.\"/>   │ │\n",
-       "│   │ │         </object>                                                                                       │ │\n",
-       "│   │ │     </list>                                                                                             │ │\n",
+       "│   │ │   <list description=\"What fees and charges are associated with my account?\" name=\"fees\"                 │ │\n",
+       "│   │ │ required=\"true\">                                                                                        │ │\n",
+       "│   │ │     <object required=\"true\">                                                                            │ │\n",
+       "│   │ │       <string format=\"guardrails/lowercase; guardrails/two_words\" name=\"name\" required=\"true\"></string> │ │\n",
+       "│   │ │       <string format=\"guardrails/one_line\" name=\"explanation\" required=\"true\"></string>                 │ │\n",
+       "│   │ │       <float description=\"The fee amount in USD or as a percentage.\" name=\"value\"                       │ │\n",
+       "│   │ │ required=\"true\"></float>                                                                                │ │\n",
+       "│   │ │     </object>                                                                                           │ │\n",
+       "│   │ │   </list>                                                                                               │ │\n",
+       "│   │ │   <list description=\"What are the interest rates offered by the bank on different kinds of accounts and │ │\n",
+       "│   │ │ products?\" name=\"interest_rates\" required=\"true\">                                                       │ │\n",
+       "│   │ │     <object required=\"true\">                                                                            │ │\n",
+       "│   │ │       <string format=\"guardrails/lowercase\" name=\"account_type\" required=\"true\"></string>               │ │\n",
+       "│   │ │       <float description=\"The annual percentage rate (APR) for the account type.\" name=\"rate\"           │ │\n",
+       "│   │ │ required=\"true\"></float>                                                                                │ │\n",
+       "│   │ │     </object>                                                                                           │ │\n",
+       "│   │ │   </list>                                                                                               │ │\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 │ │\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",
@@ -521,83 +520,83 @@
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "│   │ │ {                                                                                                       │ │\n",
-       "│   │ │     \"fees\": [                                                                                           │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"annual membership fee\",                                                            │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"my chase plan 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\": 0                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"transaction fees\",                                                                 │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"balance transfers intro fee\",                                                      │ │\n",
-       "│   │ │             \"explanation\": \"Either $5 or 3% of the amount of each transfer, whichever is greater, on    │ │\n",
+       "│   │ │   \"fees\": [                                                                                             │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"annual membership fee\",                                                                  │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"my chase plan sm 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\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"transaction fees\",                                                                       │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\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",
-       "│   │ │             \"name\": \"cash advances\",                                                                    │ │\n",
-       "│   │ │             \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\", │ │\n",
-       "│   │ │             \"value\": 10                                                                                 │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"foreign transactions\",                                                             │ │\n",
-       "│   │ │             \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                     │ │\n",
-       "│   │ │             \"value\": 3                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"penalty fees\",                                                                     │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"late payment\",                                                                     │ │\n",
-       "│   │ │             \"explanation\": \"Up to $40.\",                                                                │ │\n",
-       "│   │ │             \"value\": 40                                                                                 │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"over-the-credit-limit\",                                                            │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0                                                                                  │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"return payment\",                                                                   │ │\n",
-       "│   │ │             \"explanation\": \"Up to $40.\",                                                                │ │\n",
-       "│   │ │             \"value\": 40                                                                                 │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"name\": \"return check\",                                                                     │ │\n",
-       "│   │ │             \"explanation\": \"None\",                                                                      │ │\n",
-       "│   │ │             \"value\": 0                                                                                  │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
-       "│   │ │     ],                                                                                                  │ │\n",
-       "│   │ │     \"interest_rates\": [                                                                                 │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"account_type\": \"purchase/my chase loan/balance transfer\",                                  │ │\n",
-       "│   │ │             \"rate\": 19.49                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"account_type\": \"cash advance\",                                                             │ │\n",
-       "│   │ │             \"rate\": 29.49                                                                               │ │\n",
-       "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {                                                                                               │ │\n",
-       "│   │ │             \"account_type\": \"penalty\",                                                                  │ │\n",
-       "│   │ │             \"rate\": 29.99                                                                               │ │\n",
-       "│   │ │         }                                                                                               │ │\n",
-       "│   │ │     ]                                                                                                   │ │\n",
+       "│   │ │       \"value\": 5                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"cash advances\",                                                                          │ │\n",
+       "│   │ │       \"explanation\": \"Either $10 or 5% of the amount of each transaction, whichever is greater.\",       │ │\n",
+       "│   │ │       \"value\": 10                                                                                       │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"foreign transactions\",                                                                   │ │\n",
+       "│   │ │       \"explanation\": \"3% of the amount of each transaction in U.S. dollars.\",                           │ │\n",
+       "│   │ │       \"value\": 3                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"penalty fees\",                                                                           │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"late payment\",                                                                           │ │\n",
+       "│   │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "│   │ │       \"value\": 40                                                                                       │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"over-the-credit-limit\",                                                                  │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"return payment\",                                                                         │ │\n",
+       "│   │ │       \"explanation\": \"Up to $40.\",                                                                      │ │\n",
+       "│   │ │       \"value\": 40                                                                                       │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"name\": \"return check\",                                                                           │ │\n",
+       "│   │ │       \"explanation\": \"None\",                                                                            │ │\n",
+       "│   │ │       \"value\": 0                                                                                        │ │\n",
+       "│   │ │     }                                                                                                   │ │\n",
+       "│   │ │   ],                                                                                                    │ │\n",
+       "│   │ │   \"interest_rates\": [                                                                                   │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"account_type\": \"purchase/my chase loan/balance transfer\",                                        │ │\n",
+       "│   │ │       \"rate\": 19.49                                                                                     │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"account_type\": \"cash advance\",                                                                   │ │\n",
+       "│   │ │       \"rate\": 29.49                                                                                     │ │\n",
+       "│   │ │     },                                                                                                  │ │\n",
+       "│   │ │     {                                                                                                   │ │\n",
+       "│   │ │       \"account_type\": \"penalty\",                                                                        │ │\n",
+       "│   │ │       \"rate\": 29.99                                                                                     │ │\n",
+       "│   │ │     }                                                                                                   │ │\n",
+       "│   │ │   ]                                                                                                     │ │\n",
        "│   │ │ }                                                                                                       │ │\n",
        "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "│   │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
@@ -609,25 +608,29 @@
        "│   │ │                 fail_results=[                                                                          │ │\n",
        "│   │ │                     FailResult(                                                                         │ │\n",
        "│   │ │                         outcome='fail',                                                                 │ │\n",
-       "│   │ │                         metadata=None,                                                                  │ │\n",
        "│   │ │                         error_message='Value must be exactly two words',                                │ │\n",
-       "│   │ │                         fix_value='annual membership'                                                   │ │\n",
+       "│   │ │                         fix_value='annual membership',                                                  │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         validated_chunk=None,                                                           │ │\n",
+       "│   │ │                         error_spans=None                                                                │ │\n",
        "│   │ │                     )                                                                                   │ │\n",
        "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 0, 'name']                                                                │ │\n",
        "│   │ │             ),                                                                                          │ │\n",
        "│   │ │             'explanation': 'None',                                                                      │ │\n",
-       "│   │ │             'value': 0.0                                                                                │ │\n",
+       "│   │ │             'value': 0                                                                                  │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'name': FieldReAsk(                                                                         │ │\n",
-       "│   │ │                 incorrect_value='my chase plan fee',                                                    │ │\n",
+       "│   │ │                 incorrect_value='my chase plan sm fee',                                                 │ │\n",
        "│   │ │                 fail_results=[                                                                          │ │\n",
        "│   │ │                     FailResult(                                                                         │ │\n",
        "│   │ │                         outcome='fail',                                                                 │ │\n",
-       "│   │ │                         metadata=None,                                                                  │ │\n",
        "│   │ │                         error_message='Value must be exactly two words',                                │ │\n",
-       "│   │ │                         fix_value='my chase'                                                            │ │\n",
+       "│   │ │                         fix_value='my chase',                                                           │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         validated_chunk=None,                                                           │ │\n",
+       "│   │ │                         error_spans=None                                                                │ │\n",
        "│   │ │                     )                                                                                   │ │\n",
        "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 1, 'name']                                                                │ │\n",
@@ -637,18 +640,20 @@
        "│   │ │ 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': 0.0                                                                                │ │\n",
+       "│   │ │             'value': 0                                                                                  │ │\n",
        "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {'name': 'transaction fees', 'explanation': 'None', 'value': 0.0},                              │ │\n",
+       "│   │ │         {'name': 'transaction fees', 'explanation': 'None', 'value': 0},                                │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'name': FieldReAsk(                                                                         │ │\n",
        "│   │ │                 incorrect_value='balance transfers intro fee',                                          │ │\n",
        "│   │ │                 fail_results=[                                                                          │ │\n",
        "│   │ │                     FailResult(                                                                         │ │\n",
        "│   │ │                         outcome='fail',                                                                 │ │\n",
-       "│   │ │                         metadata=None,                                                                  │ │\n",
        "│   │ │                         error_message='Value must be exactly two words',                                │ │\n",
-       "│   │ │                         fix_value='balance transfers'                                                   │ │\n",
+       "│   │ │                         fix_value='balance transfers',                                                  │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         validated_chunk=None,                                                           │ │\n",
+       "│   │ │                         error_spans=None                                                                │ │\n",
        "│   │ │                     )                                                                                   │ │\n",
        "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 3, 'name']                                                                │ │\n",
@@ -668,7 +673,7 @@
        "│   │ │             'explanation': '3% of the amount of each transaction in U.S. dollars.',                     │ │\n",
        "│   │ │             'value': 3.0                                                                                │ │\n",
        "│   │ │         },                                                                                              │ │\n",
-       "│   │ │         {'name': 'penalty fees', 'explanation': 'None', 'value': 0.0},                                  │ │\n",
+       "│   │ │         {'name': 'penalty fees', 'explanation': 'None', 'value': 0},                                    │ │\n",
        "│   │ │         {'name': 'late payment', 'explanation': 'Up to $40.', 'value': 40.0},                           │ │\n",
        "│   │ │         {                                                                                               │ │\n",
        "│   │ │             'name': FieldReAsk(                                                                         │ │\n",
@@ -676,18 +681,20 @@
        "│   │ │                 fail_results=[                                                                          │ │\n",
        "│   │ │                     FailResult(                                                                         │ │\n",
        "│   │ │                         outcome='fail',                                                                 │ │\n",
-       "│   │ │                         metadata=None,                                                                  │ │\n",
        "│   │ │                         error_message='Value must be exactly two words',                                │ │\n",
-       "│   │ │                         fix_value='over the'                                                            │ │\n",
+       "│   │ │                         fix_value='over the',                                                           │ │\n",
+       "│   │ │                         metadata=None,                                                                  │ │\n",
+       "│   │ │                         validated_chunk=None,                                                           │ │\n",
+       "│   │ │                         error_spans=None                                                                │ │\n",
        "│   │ │                     )                                                                                   │ │\n",
        "│   │ │                 ],                                                                                      │ │\n",
        "│   │ │                 path=['fees', 8, 'name']                                                                │ │\n",
        "│   │ │             ),                                                                                          │ │\n",
        "│   │ │             'explanation': 'None',                                                                      │ │\n",
-       "│   │ │             'value': 0.0                                                                                │ │\n",
+       "│   │ │             'value': 0                                                                                  │ │\n",
        "│   │ │         },                                                                                              │ │\n",
        "│   │ │         {'name': 'return payment', 'explanation': 'Up to $40.', 'value': 40.0},                         │ │\n",
-       "│   │ │         {'name': 'return check', 'explanation': 'None', 'value': 0.0}                                   │ │\n",
+       "│   │ │         {'name': 'return check', 'explanation': 'None', 'value': 0}                                     │ │\n",
        "│   │ │     ],                                                                                                  │ │\n",
        "│   │ │     'interest_rates': [                                                                                 │ │\n",
        "│   │ │         {                                                                                               │ │\n",
@@ -715,11 +722,11 @@
        "    │ │         ]                                                                                               │ │\n",
        "    │ │       },                                                                                                │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": {                                                                                         │ │\n",
-       "    │ │         \"incorrect_value\": \"my chase plan fee\",                                                         │ │\n",
+       "    │ │         \"incorrect_value\": \"my chase plan sm fee\",                                                      │ │\n",
        "    │ │         \"error_messages\": [                                                                             │ │\n",
        "    │ │           \"Value must be exactly two words\"                                                             │ │\n",
        "    │ │         ]                                                                                               │ │\n",
@@ -729,12 +736,12 @@
        "    │ │ 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\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"transaction fees\",                                                                       │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": {                                                                                         │ │\n",
@@ -761,7 +768,7 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"penalty fees\",                                                                           │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"late payment\",                                                                           │ │\n",
@@ -776,7 +783,7 @@
        "    │ │         ]                                                                                               │ │\n",
        "    │ │       },                                                                                                │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"return payment\",                                                                         │ │\n",
@@ -786,7 +793,7 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"return check\",                                                                           │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     }                                                                                                   │ │\n",
        "    │ │   ],                                                                                                    │ │\n",
        "    │ │   \"interest_rates\": [                                                                                   │ │\n",
@@ -811,23 +818,25 @@
        "    │ │ it into.                                                                                                │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ <output>                                                                                                │ │\n",
-       "    │ │     <list name=\"fees\" description=\"What fees and charges are associated with my account?\">              │ │\n",
-       "    │ │         <object>                                                                                        │ │\n",
-       "    │ │             <string name=\"name\" format=\"guardrails/lowercase; guardrails/two_words\"/>                   │ │\n",
-       "    │ │             <string name=\"explanation\" format=\"guardrails/one_line\"/>                                   │ │\n",
-       "    │ │             <float name=\"value\" description=\"The fee amount in USD or as a percentage.\"/>               │ │\n",
-       "    │ │         </object>                                                                                       │ │\n",
-       "    │ │     </list>                                                                                             │ │\n",
-       "    │ │     <list name=\"interest_rates\" description=\"What are the interest rates offered by the bank on         │ │\n",
-       "    │ │ different kinds of accounts and products?\">                                                             │ │\n",
-       "    │ │         <object>                                                                                        │ │\n",
-       "    │ │             <string name=\"account_type\" format=\"guardrails/lowercase\"/>                                 │ │\n",
-       "    │ │             <float name=\"rate\" description=\"The annual percentage rate (APR) for the account type.\"/>   │ │\n",
-       "    │ │         </object>                                                                                       │ │\n",
-       "    │ │     </list>                                                                                             │ │\n",
+       "    │ │   <list description=\"What fees and charges are associated with my account?\" name=\"fees\"                 │ │\n",
+       "    │ │ required=\"true\">                                                                                        │ │\n",
+       "    │ │     <object required=\"true\">                                                                            │ │\n",
+       "    │ │       <string format=\"guardrails/lowercase; guardrails/two_words\" name=\"name\" required=\"true\"></string> │ │\n",
+       "    │ │       <string format=\"guardrails/one_line\" name=\"explanation\" required=\"true\"></string>                 │ │\n",
+       "    │ │       <float description=\"The fee amount in USD or as a percentage.\" name=\"value\"                       │ │\n",
+       "    │ │ required=\"true\"></float>                                                                                │ │\n",
+       "    │ │     </object>                                                                                           │ │\n",
+       "    │ │   </list>                                                                                               │ │\n",
+       "    │ │   <list description=\"What are the interest rates offered by the bank on different kinds of accounts and │ │\n",
+       "    │ │ products?\" name=\"interest_rates\" required=\"true\">                                                       │ │\n",
+       "    │ │     <object required=\"true\">                                                                            │ │\n",
+       "    │ │       <string format=\"guardrails/lowercase\" name=\"account_type\" required=\"true\"></string>               │ │\n",
+       "    │ │       <float description=\"The annual percentage rate (APR) for the account type.\" name=\"rate\"           │ │\n",
+       "    │ │ required=\"true\"></float>                                                                                │ │\n",
+       "    │ │     </object>                                                                                           │ │\n",
+       "    │ │   </list>                                                                                               │ │\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 │ │\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",
@@ -848,21 +857,21 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"annual membership fee\",                                                                  │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
-       "    │ │       \"name\": \"my chase plan fee\",                                                                      │ │\n",
+       "    │ │       \"name\": \"my chase plan sm 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\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"transaction fees\",                                                                       │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"balance transfers intro fee\",                                                            │ │\n",
@@ -884,7 +893,7 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"penalty fees\",                                                                           │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"late payment\",                                                                           │ │\n",
@@ -894,7 +903,7 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"over-the-credit-limit\",                                                                  │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     },                                                                                                  │ │\n",
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"return payment\",                                                                         │ │\n",
@@ -904,7 +913,7 @@
        "    │ │     {                                                                                                   │ │\n",
        "    │ │       \"name\": \"return check\",                                                                           │ │\n",
        "    │ │       \"explanation\": \"None\",                                                                            │ │\n",
-       "    │ │       \"value\": 0.0                                                                                      │ │\n",
+       "    │ │       \"value\": 0                                                                                        │ │\n",
        "    │ │     }                                                                                                   │ │\n",
        "    │ │   ],                                                                                                    │ │\n",
        "    │ │   \"interest_rates\": [                                                                                   │ │\n",
@@ -926,7 +935,7 @@
        "    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
        "    │ │ {                                                                                                       │ │\n",
        "    │ │     'fees': [                                                                                           │ │\n",
-       "    │ │         {'name': 'annual membership', 'explanation': 'None', 'value': 0.0},                             │ │\n",
+       "    │ │         {'name': 'annual membership', 'explanation': 'None', 'value': 0},                               │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'name': 'my chase',                                                                         │ │\n",
        "    │ │             'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction or    │ │\n",
@@ -934,9 +943,9 @@
        "    │ │ 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': 0.0                                                                                │ │\n",
+       "    │ │             'value': 0                                                                                  │ │\n",
        "    │ │         },                                                                                              │ │\n",
-       "    │ │         {'name': 'transaction fees', 'explanation': 'None', 'value': 0.0},                              │ │\n",
+       "    │ │         {'name': 'transaction fees', 'explanation': 'None', 'value': 0},                                │ │\n",
        "    │ │         {                                                                                               │ │\n",
        "    │ │             'name': 'balance transfers',                                                                │ │\n",
        "    │ │             'explanation': 'Either $5 or 3% of the amount of each transfer, whichever is greater, on    │ │\n",
@@ -954,11 +963,11 @@
        "    │ │             'explanation': '3% of the amount of each transaction in U.S. dollars.',                     │ │\n",
        "    │ │             'value': 3.0                                                                                │ │\n",
        "    │ │         },                                                                                              │ │\n",
-       "    │ │         {'name': 'penalty fees', 'explanation': 'None', 'value': 0.0},                                  │ │\n",
+       "    │ │         {'name': 'penalty fees', 'explanation': 'None', 'value': 0},                                    │ │\n",
        "    │ │         {'name': 'late payment', 'explanation': 'Up to $40.', 'value': 40.0},                           │ │\n",
-       "    │ │         {'name': 'over the', 'explanation': 'None', 'value': 0.0},                                      │ │\n",
+       "    │ │         {'name': 'over the', 'explanation': 'None', 'value': 0},                                        │ │\n",
        "    │ │         {'name': 'return payment', 'explanation': 'Up to $40.', 'value': 40.0},                         │ │\n",
-       "    │ │         {'name': 'return check', 'explanation': 'None', 'value': 0.0}                                   │ │\n",
+       "    │ │         {'name': 'return check', 'explanation': 'None', 'value': 0}                                     │ │\n",
        "    │ │     ],                                                                                                  │ │\n",
        "    │ │     'interest_rates': [                                                                                 │ │\n",
        "    │ │         {                                                                                               │ │\n",
@@ -1109,23 +1118,25 @@
        "│   │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "│   │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1149,83 +1160,83 @@
        "│   │ \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 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            \"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 \u001b[0m\u001b[48;2;245;245;220m  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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\": 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\": \"transaction 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            \"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            \"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;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 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      \"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 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\": 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\": \"transaction 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      \"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      \"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            \"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\": 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        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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            \"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            \"name\": \"penalty 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            \"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            \"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            \"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\": 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 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            \"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\": 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    \"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        {\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
-       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m            \"account_type\": \"purchase/my chase loan/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            \"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        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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            \"account_type\": \"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            \"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        },\u001b[0m\u001b[48;2;245;245;220m                                                                                             \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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            \"account_type\": \"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            \"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        }\u001b[0m\u001b[48;2;245;245;220m                                                                                              \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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      \"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      \"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\": 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    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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      \"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      \"name\": \"penalty 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      \"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      \"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      \"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\": 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 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      \"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\": 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  \"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    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n",
+       "│   │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m      \"account_type\": \"purchase/my chase loan/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      \"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    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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      \"account_type\": \"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      \"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    },\u001b[0m\u001b[48;2;245;245;220m                                                                                                 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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      \"account_type\": \"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      \"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    }\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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",
@@ -1237,25 +1248,29 @@
        "│   │ \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 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                        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                        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                        validated_chunk=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_spans=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=['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            '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",
        "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 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                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                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 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                        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                        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                        validated_chunk=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_spans=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=['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",
@@ -1265,18 +1280,20 @@
        "│   │ \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': 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            '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",
-       "│   │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {'name': 'transaction fees', 'explanation': 'None', '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        {'name': 'transaction fees', 'explanation': 'None', '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",
        "│   │ \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='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                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 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                        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                        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                        validated_chunk=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_spans=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=['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",
@@ -1296,7 +1313,7 @@
        "│   │ \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        {'name': 'penalty fees', 'explanation': 'None', '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        {'name': 'penalty fees', 'explanation': 'None', '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        {'name': 'late payment', 'explanation': 'Up to $40.', '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            '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",
@@ -1304,18 +1321,20 @@
        "│   │ \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 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'\u001b[0m\u001b[48;2;240;255;240m                                                           \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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',\u001b[0m\u001b[48;2;240;255;240m                                                          \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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                        validated_chunk=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_spans=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=['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': 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            '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",
        "│   │ \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', 'explanation': 'Up to $40.', '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        {'name': 'return check', 'explanation': 'None', '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        {'name': 'return check', 'explanation': 'None', '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",
        "│   │ \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        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1343,11 +1362,11 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        ]\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      },\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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      \"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      \"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 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        \"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_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 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",
@@ -1357,12 +1376,12 @@
        "    │ \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\": 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      \"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      \"name\": \"transaction 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      \"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      \"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      \"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",
@@ -1389,7 +1408,7 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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\": \"penalty 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      \"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      \"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      \"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",
@@ -1404,7 +1423,7 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m        ]\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      },\u001b[0m\u001b[48;2;240;248;255m                                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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      \"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      \"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",
@@ -1414,7 +1433,7 @@
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    {\u001b[0m\u001b[48;2;240;248;255m                                                                                                  \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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\": 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      \"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",
@@ -1439,23 +1458,25 @@
        "    │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                           \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m                                                                               \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m    \u001b[0m\u001b[48;2;240;248;255m                                                                                          \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
+       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m  \u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m                                                                                              \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
-       "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m                                                                                                       \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
        "    │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m      \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n",
@@ -1476,21 +1497,21 @@
        "    │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m    {\u001b[0m\u001b[48;2;245;245;220m                                                                                                  \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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      \"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\": \"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      \"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 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\": 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      \"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\": \"transaction 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      \"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      \"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\": \"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",
@@ -1512,7 +1533,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      \"name\": \"penalty 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      \"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      \"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\": \"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",
@@ -1522,7 +1543,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      \"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\": 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      \"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\": \"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",
@@ -1532,7 +1553,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      \"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\": 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      \"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  \"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",
@@ -1554,7 +1575,7 @@
        "    │ \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        {'name': 'annual membership', 'explanation': 'None', '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        {'name': 'annual membership', 'explanation': 'None', '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",
        "    │ \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",
@@ -1562,9 +1583,9 @@
        "    │ \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': 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            '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",
-       "    │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m        {'name': 'transaction fees', 'explanation': 'None', '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        {'name': 'transaction fees', 'explanation': 'None', '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",
        "    │ \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': 'Either $5 or 3% of the amount of each transfer, whichever is greater, 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",
@@ -1582,11 +1603,11 @@
        "    │ \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        {'name': 'penalty fees', 'explanation': 'None', '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        {'name': 'penalty fees', 'explanation': 'None', '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        {'name': 'late payment', 'explanation': 'Up to $40.', '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        {'name': 'over the', 'explanation': 'None', '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        {'name': 'over the', 'explanation': 'None', '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        {'name': 'return payment', 'explanation': 'Up to $40.', '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        {'name': 'return check', 'explanation': 'None', '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        {'name': 'return check', 'explanation': 'None', '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",
        "    │ \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        {\u001b[0m\u001b[48;2;240;255;240m                                                                                              \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n",
@@ -1601,7 +1622,7 @@
        "    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n"
       ]
      },
-     "execution_count": 136,
+     "execution_count": 19,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -1627,7 +1648,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.10.0"
+   "version": "3.12.3"
   },
   "vscode": {
    "interpreter": {
diff --git a/docs/examples/generate_structured_data.ipynb b/docs/examples/generate_structured_data.ipynb
index ee80c9d24..890b44899 100644
--- a/docs/examples/generate_structured_data.ipynb
+++ b/docs/examples/generate_structured_data.ipynb
@@ -2,7 +2,7 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 3,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -61,9 +61,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 4,
+   "execution_count": 1,
    "metadata": {},
-   "outputs": [],
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
+      "  from tqdm.autonotebook import tqdm, trange\n"
+     ]
+    }
+   ],
    "source": [
     "from pydantic import BaseModel, Field\n",
     "from guardrails.hub import ValidLength, TwoWords, ValidRange\n",
@@ -73,7 +82,7 @@
     "prompt = \"\"\"\n",
     "Generate a dataset of fake user orders. Each row of the dataset should be valid.\n",
     "\n",
-    "${gr.complete_json_suffix}\n",
+    "${gr.complete_xml_suffix}\n",
     "\"\"\"\n",
     "\n",
     "class Order(BaseModel):\n",
@@ -109,7 +118,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -120,11 +129,35 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
-    "guard = gd.Guard.from_pydantic(output_class=Orders, prompt=prompt)"
+    "guard = gd.Guard.from_pydantic(output_class=Orders)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Step 3: Wrap the LLM API call with `Guard`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import openai\n",
+    "\n",
+    "\n",
+    "res = guard(\n",
+    "    openai.chat.completions.create,\n",
+    "    prompt=prompt,\n",
+    "    max_tokens=2048,\n",
+    "    temperature=0\n",
+    ")\n"
    ]
   },
   {
@@ -136,7 +169,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 7,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -149,19 +182,18 @@
        "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=\"guardrails/valid_length: min=10 max=10\">\n",
-       "        <object>\n",
-       "            <string name=\"user_id\" description=\"The user's id.\"/>\n",
-       "            <string name=\"user_name\" description=\"The user's first name and last name\" \n",
-       "format=\"guardrails/two_words\"/>\n",
-       "            <integer name=\"num_orders\" description=\"The number of orders the user has placed\" \n",
-       "format=\"guardrails/valid_range: min=0 max=50\"/>\n",
-       "        </object>\n",
-       "    </list>\n",
+       "  <list description=\"Generate a list of user, and how many orders they have placed in the past.\" \n",
+       "format=\"guardrails/valid_length: 10 10\" name=\"user_orders\" required=\"true\">\n",
+       "    <object format=\"guardrails/valid_length: 10 10\" required=\"true\">\n",
+       "      <string description=\"The user's id.\" name=\"user_id\" required=\"true\"></string>\n",
+       "      <string description=\"The user's first name and last name\" format=\"guardrails/two_words\" name=\"user_name\" \n",
+       "required=\"true\"></string>\n",
+       "      <integer description=\"The number of orders the user has placed\" format=\"guardrails/valid_range: 0 50\" \n",
+       "name=\"num_orders\" required=\"true\"></integer>\n",
+       "    </object>\n",
+       "  </list>\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",
@@ -184,19 +216,18 @@
        "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\u001b[35m/\u001b[0m\u001b[95mlist\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[95mstring\u001b[0m\u001b[39m>\u001b[0m\n",
+       "\u001b[39m      <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n",
+       "\u001b[39m      <\u001b[0m\u001b[35m/\u001b[0m\u001b[95minteger\u001b[0m\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",
        "\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",
@@ -216,51 +247,66 @@
     }
    ],
    "source": [
-    "print(guard.rail.prompt)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Step 3: Wrap the LLM API call with `Guard`"
+    "print(guard.history.last.compiled_prompt)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 8,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "
{\"user_orders\":[{\"user_id\":\"u123\",\"user_name\":\"John Doe\",\"num_orders\":5},{\"user_id\":\"u456\",\"user_name\":\"Jane \n",
+       "Smith\",\"num_orders\":10},{\"user_id\":\"u789\",\"user_name\":\"Alice \n",
+       "Johnson\",\"num_orders\":3},{\"user_id\":\"u246\",\"user_name\":\"Bob \n",
+       "Brown\",\"num_orders\":20},{\"user_id\":\"u135\",\"user_name\":\"Emily \n",
+       "Davis\",\"num_orders\":8},{\"user_id\":\"u579\",\"user_name\":\"Michael \n",
+       "Wilson\",\"num_orders\":15},{\"user_id\":\"u357\",\"user_name\":\"Sarah \n",
+       "Lee\",\"num_orders\":7},{\"user_id\":\"u852\",\"user_name\":\"David \n",
+       "Moore\",\"num_orders\":12},{\"user_id\":\"u963\",\"user_name\":\"Laura \n",
+       "Taylor\",\"num_orders\":4},{\"user_id\":\"u741\",\"user_name\":\"Chris Anderson\",\"num_orders\":6}]}\n",
+       "
\n" + ], + "text/plain": [ + "\u001b[1m{\u001b[0m\u001b[32m\"user_orders\"\u001b[0m:\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u123\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"John Doe\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m5\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u456\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Jane \u001b[0m\n", + "\u001b[32mSmith\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m10\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u789\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Alice \u001b[0m\n", + "\u001b[32mJohnson\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u246\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Bob \u001b[0m\n", + "\u001b[32mBrown\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m20\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u135\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Emily \u001b[0m\n", + "\u001b[32mDavis\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m8\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u579\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Michael \u001b[0m\n", + "\u001b[32mWilson\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m15\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u357\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Sarah \u001b[0m\n", + "\u001b[32mLee\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m7\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u852\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"David \u001b[0m\n", + "\u001b[32mMoore\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m12\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u963\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Laura \u001b[0m\n", + "\u001b[32mTaylor\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m4\u001b[0m\u001b[1m}\u001b[0m,\u001b[1m{\u001b[0m\u001b[32m\"user_id\"\u001b[0m:\u001b[32m\"u741\"\u001b[0m,\u001b[32m\"user_name\"\u001b[0m:\u001b[32m\"Chris Anderson\"\u001b[0m,\u001b[32m\"num_orders\"\u001b[0m:\u001b[1;36m6\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\u001b[1m}\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/plain": [ "{'user_orders': [{'user_id': 'u123', 'user_name': 'John Doe', 'num_orders': 5},\n", - " {'user_id': 'u456', 'user_name': 'Jane Smith', 'num_orders': 12},\n", + " {'user_id': 'u456', 'user_name': 'Jane Smith', 'num_orders': 10},\n", " {'user_id': 'u789', 'user_name': 'Alice Johnson', 'num_orders': 3},\n", - " {'user_id': 'u234', 'user_name': 'Michael Brown', 'num_orders': 8},\n", - " {'user_id': 'u567', 'user_name': 'Emily Davis', 'num_orders': 20},\n", - " {'user_id': 'u890', 'user_name': 'David Wilson', 'num_orders': 15},\n", - " {'user_id': 'u345', 'user_name': 'Sarah Martinez', 'num_orders': 7},\n", - " {'user_id': 'u678', 'user_name': 'Robert Anderson', 'num_orders': 10},\n", - " {'user_id': 'u901', 'user_name': 'Laura Thompson', 'num_orders': 2},\n", - " {'user_id': 'u432', 'user_name': 'William Garcia', 'num_orders': 18}]}" + " {'user_id': 'u246', 'user_name': 'Bob Brown', 'num_orders': 20},\n", + " {'user_id': 'u135', 'user_name': 'Emily Davis', 'num_orders': 8},\n", + " {'user_id': 'u579', 'user_name': 'Michael Wilson', 'num_orders': 15},\n", + " {'user_id': 'u357', 'user_name': 'Sarah Lee', 'num_orders': 7},\n", + " {'user_id': 'u852', 'user_name': 'David Moore', 'num_orders': 12},\n", + " {'user_id': 'u963', 'user_name': 'Laura Taylor', 'num_orders': 4},\n", + " {'user_id': 'u741', 'user_name': 'Chris Anderson', 'num_orders': 6}]}" ] }, - "execution_count": 8, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import openai\n", - "\n", - "\n", - "res = guard(\n", - " openai.chat.completions.create,\n", - " max_tokens=2048,\n", - " temperature=0\n", - ")\n", - "res.validated_output\n" + "print(res.raw_llm_output)\n", + "res.validated_output" ] }, { @@ -275,7 +321,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -292,19 +338,18 @@ " │ │ 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=\"guardrails/valid_length: min=10 max=10\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <string name=\"user_id\" description=\"The user's id.\"/> │ │\n", - " │ │ <string name=\"user_name\" description=\"The user's first name and last name\" │ │\n", - " │ │ format=\"guardrails/two_words\"/> │ │\n", - " │ │ <integer name=\"num_orders\" description=\"The number of orders the user has placed\" │ │\n", - " │ │ format=\"guardrails/valid_range: min=0 max=50\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", + " │ │ <list description=\"Generate a list of user, and how many orders they have placed in the past.\" │ │\n", + " │ │ format=\"guardrails/valid_length: 10 10\" name=\"user_orders\" required=\"true\"> │ │\n", + " │ │ <object format=\"guardrails/valid_length: 10 10\" required=\"true\"> │ │\n", + " │ │ <string description=\"The user's id.\" name=\"user_id\" required=\"true\"></string> │ │\n", + " │ │ <string description=\"The user's first name and last name\" format=\"guardrails/two_words\" │ │\n", + " │ │ name=\"user_name\" required=\"true\"></string> │ │\n", + " │ │ <integer description=\"The number of orders the user has placed\" format=\"guardrails/valid_range: 0 │ │\n", + " │ │ 50\" name=\"num_orders\" required=\"true\"></integer> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\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 │ │\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", @@ -330,28 +375,28 @@ " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", " │ │ {\"user_orders\":[{\"user_id\":\"u123\",\"user_name\":\"John │ │\n", " │ │ Doe\",\"num_orders\":5},{\"user_id\":\"u456\",\"user_name\":\"Jane │ │\n", - " │ │ Smith\",\"num_orders\":12},{\"user_id\":\"u789\",\"user_name\":\"Alice │ │\n", - " │ │ Johnson\",\"num_orders\":3},{\"user_id\":\"u234\",\"user_name\":\"Michael │ │\n", - " │ │ Brown\",\"num_orders\":8},{\"user_id\":\"u567\",\"user_name\":\"Emily │ │\n", - " │ │ Davis\",\"num_orders\":20},{\"user_id\":\"u890\",\"user_name\":\"David │ │\n", - " │ │ Wilson\",\"num_orders\":15},{\"user_id\":\"u345\",\"user_name\":\"Sarah │ │\n", - " │ │ Martinez\",\"num_orders\":7},{\"user_id\":\"u678\",\"user_name\":\"Robert │ │\n", - " │ │ Anderson\",\"num_orders\":10},{\"user_id\":\"u901\",\"user_name\":\"Laura │ │\n", - " │ │ Thompson\",\"num_orders\":2},{\"user_id\":\"u432\",\"user_name\":\"William Garcia\",\"num_orders\":18}]} │ │\n", + " │ │ Smith\",\"num_orders\":10},{\"user_id\":\"u789\",\"user_name\":\"Alice │ │\n", + " │ │ Johnson\",\"num_orders\":3},{\"user_id\":\"u246\",\"user_name\":\"Bob │ │\n", + " │ │ Brown\",\"num_orders\":20},{\"user_id\":\"u135\",\"user_name\":\"Emily │ │\n", + " │ │ Davis\",\"num_orders\":8},{\"user_id\":\"u579\",\"user_name\":\"Michael │ │\n", + " │ │ Wilson\",\"num_orders\":15},{\"user_id\":\"u357\",\"user_name\":\"Sarah │ │\n", + " │ │ Lee\",\"num_orders\":7},{\"user_id\":\"u852\",\"user_name\":\"David │ │\n", + " │ │ Moore\",\"num_orders\":12},{\"user_id\":\"u963\",\"user_name\":\"Laura │ │\n", + " │ │ Taylor\",\"num_orders\":4},{\"user_id\":\"u741\",\"user_name\":\"Chris Anderson\",\"num_orders\":6}]} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", " │ │ 'user_orders': [ │ │\n", " │ │ {'user_id': 'u123', 'user_name': 'John Doe', 'num_orders': 5}, │ │\n", - " │ │ {'user_id': 'u456', 'user_name': 'Jane Smith', 'num_orders': 12}, │ │\n", + " │ │ {'user_id': 'u456', 'user_name': 'Jane Smith', 'num_orders': 10}, │ │\n", " │ │ {'user_id': 'u789', 'user_name': 'Alice Johnson', 'num_orders': 3}, │ │\n", - " │ │ {'user_id': 'u234', 'user_name': 'Michael Brown', 'num_orders': 8}, │ │\n", - " │ │ {'user_id': 'u567', 'user_name': 'Emily Davis', 'num_orders': 20}, │ │\n", - " │ │ {'user_id': 'u890', 'user_name': 'David Wilson', 'num_orders': 15}, │ │\n", - " │ │ {'user_id': 'u345', 'user_name': 'Sarah Martinez', 'num_orders': 7}, │ │\n", - " │ │ {'user_id': 'u678', 'user_name': 'Robert Anderson', 'num_orders': 10}, │ │\n", - " │ │ {'user_id': 'u901', 'user_name': 'Laura Thompson', 'num_orders': 2}, │ │\n", - " │ │ {'user_id': 'u432', 'user_name': 'William Garcia', 'num_orders': 18} │ │\n", + " │ │ {'user_id': 'u246', 'user_name': 'Bob Brown', 'num_orders': 20}, │ │\n", + " │ │ {'user_id': 'u135', 'user_name': 'Emily Davis', 'num_orders': 8}, │ │\n", + " │ │ {'user_id': 'u579', 'user_name': 'Michael Wilson', 'num_orders': 15}, │ │\n", + " │ │ {'user_id': 'u357', 'user_name': 'Sarah Lee', 'num_orders': 7}, │ │\n", + " │ │ {'user_id': 'u852', 'user_name': 'David Moore', 'num_orders': 12}, │ │\n", + " │ │ {'user_id': 'u963', 'user_name': 'Laura Taylor', 'num_orders': 4}, │ │\n", + " │ │ {'user_id': 'u741', 'user_name': 'Chris Anderson', 'num_orders': 6} │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -370,19 +415,18 @@ " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -408,28 +452,28 @@ " │ \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{\"user_orders\":[{\"user_id\":\"u123\",\"user_name\":\"John \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mDoe\",\"num_orders\":5},{\"user_id\":\"u456\",\"user_name\":\"Jane \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mSmith\",\"num_orders\":12},{\"user_id\":\"u789\",\"user_name\":\"Alice \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mJohnson\",\"num_orders\":3},{\"user_id\":\"u234\",\"user_name\":\"Michael \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mBrown\",\"num_orders\":8},{\"user_id\":\"u567\",\"user_name\":\"Emily \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mDavis\",\"num_orders\":20},{\"user_id\":\"u890\",\"user_name\":\"David \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mWilson\",\"num_orders\":15},{\"user_id\":\"u345\",\"user_name\":\"Sarah \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mMartinez\",\"num_orders\":7},{\"user_id\":\"u678\",\"user_name\":\"Robert \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mAnderson\",\"num_orders\":10},{\"user_id\":\"u901\",\"user_name\":\"Laura \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mThompson\",\"num_orders\":2},{\"user_id\":\"u432\",\"user_name\":\"William Garcia\",\"num_orders\":18}]}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mSmith\",\"num_orders\":10},{\"user_id\":\"u789\",\"user_name\":\"Alice \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mJohnson\",\"num_orders\":3},{\"user_id\":\"u246\",\"user_name\":\"Bob \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mBrown\",\"num_orders\":20},{\"user_id\":\"u135\",\"user_name\":\"Emily \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mDavis\",\"num_orders\":8},{\"user_id\":\"u579\",\"user_name\":\"Michael \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mWilson\",\"num_orders\":15},{\"user_id\":\"u357\",\"user_name\":\"Sarah \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mLee\",\"num_orders\":7},{\"user_id\":\"u852\",\"user_name\":\"David \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mMoore\",\"num_orders\":12},{\"user_id\":\"u963\",\"user_name\":\"Laura \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mTaylor\",\"num_orders\":4},{\"user_id\":\"u741\",\"user_name\":\"Chris Anderson\",\"num_orders\":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 │\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 'user_orders': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'u123', 'user_name': 'John Doe', '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 {'user_id': 'u456', 'user_name': 'Jane Smith', 'num_orders': 12},\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'u456', 'user_name': 'Jane Smith', '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 {'user_id': 'u789', 'user_name': 'Alice Johnson', 'num_orders': 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_id': 'u234', 'user_name': 'Michael Brown', 'num_orders': 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_id': 'u567', 'user_name': 'Emily Davis', '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_id': 'u890', 'user_name': 'David Wilson', '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 {'user_id': 'u345', 'user_name': 'Sarah Martinez', 'num_orders': 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_id': 'u678', 'user_name': 'Robert Anderson', '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 {'user_id': 'u901', 'user_name': 'Laura Thompson', 'num_orders': 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_id': 'u432', 'user_name': 'William Garcia', 'num_orders': 18}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'u246', 'user_name': 'Bob Brown', '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_id': 'u135', 'user_name': 'Emily Davis', 'num_orders': 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_id': 'u579', 'user_name': 'Michael Wilson', '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 {'user_id': 'u357', 'user_name': 'Sarah Lee', 'num_orders': 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_id': 'u852', 'user_name': 'David Moore', 'num_orders': 12},\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'u963', 'user_name': 'Laura Taylor', 'num_orders': 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_id': 'u741', 'user_name': 'Chris Anderson', 'num_orders': 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 ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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", @@ -461,7 +505,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/examples/generate_structured_data_cohere.ipynb b/docs/examples/generate_structured_data_cohere.ipynb index e22624ea8..837552847 100644 --- a/docs/examples/generate_structured_data_cohere.ipynb +++ b/docs/examples/generate_structured_data_cohere.ipynb @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "6a7c7d4a", "metadata": {}, "outputs": [ @@ -68,7 +68,7 @@ "!guardrails hub install hub://guardrails/valid_length --quiet\n", "!guardrails hub install hub://guardrails/two_words --quiet\n", "!guardrails hub install hub://guardrails/valid_range --quiet\n", - "!pip install cohere==5.3.2 --quiet" + "!pip install cohere --quiet" ] }, { @@ -81,10 +81,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "3088fd99", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ "from pydantic import BaseModel, Field\n", "from guardrails.hub import ValidLength, TwoWords, ValidRange\n", @@ -121,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "840006ca-21ca-4f76-9ce1-e406d5d68412", "metadata": {}, "outputs": [], @@ -144,7 +153,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "id": "42766922-14d0-4b5e-853a-23f05b896a09", "metadata": {}, "outputs": [ @@ -152,19 +161,21 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/validatorsattr.py:307: UserWarning: Validator 1-indexed is not installed!\n", - " warnings.warn(f\"Validator {validator_name} is not installed!\")\n" + "WARNING:guardrails-ai:Validator with id 1-indexed was not found in the registry! Ignoring...\n", + "WARNING:guardrails-ai:Invalid arguments! ('1-indexed', 'noop')\n" ] } ], "source": [ + "from rich import print\n", "import guardrails as gd\n", - "guard = gd.Guard.from_pydantic(output_class=Orders, prompt=prompt)\n", + "guard = gd.Guard.from_pydantic(output_class=Orders)\n", "\n", "res = co.chat(message=\"hi\")\n", "\n", "raw_llm_response, validated_response, *rest = guard(\n", "\tco.chat,\n", + " \tprompt=prompt,\n", "\tmodel=\"command\",\n", "\tmax_tokens=1024,\n", "\ttemperature=0.3\n", @@ -199,19 +210,18 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <list name=\"user_orders\" description=\"Generate a list of users and how many orders they have placed │ │\n", - " │ │ in the past.\" format=\"guardrails/valid_length: min=10 max=10\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <integer 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=\"guardrails/two_words\"/> │ │\n", - " │ │ <integer name=\"num_orders\" description=\"The number of orders the user has placed\" │ │\n", - " │ │ format=\"guardrails/valid_range: min=0 max=50\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", + " │ │ <list description=\"Generate a list of users and how many orders they have placed in the past.\" │ │\n", + " │ │ format=\"guardrails/valid_length: 10 10\" name=\"user_orders\" required=\"true\"> │ │\n", + " │ │ <object format=\"guardrails/valid_length: 10 10\" required=\"true\"> │ │\n", + " │ │ <integer description=\"The user's id.\" name=\"user_id\" required=\"true\"></integer> │ │\n", + " │ │ <string description=\"The user's first name and last name\" format=\"guardrails/two_words\" │ │\n", + " │ │ name=\"user_name\" required=\"true\"></string> │ │\n", + " │ │ <integer description=\"The number of orders the user has placed\" format=\"guardrails/valid_range: 0 │ │\n", + " │ │ 50\" name=\"num_orders\" required=\"true\"></integer> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\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 │ │\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", @@ -246,53 +256,43 @@ " │ │ \"user_orders\": [ │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 1, │ │\n", - " │ │ \"user_name\": \"John Mcdonald\", │ │\n", - " │ │ \"num_orders\": 6 │ │\n", + " │ │ \"user_name\": \"Fiona Gupta\", │ │\n", + " │ │ \"num_orders\": 8 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 2, │ │\n", - " │ │ \"user_name\": \"Jane Smith\", │ │\n", - " │ │ \"num_orders\": 4 │ │\n", + " │ │ \"user_name\": \"Roger Mcdonald\", │ │\n", + " │ │ \"num_orders\": 10 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 3, │ │\n", - " │ │ \"user_name\": \"David Lee\", │ │\n", - " │ │ \"num_orders\": 2 │ │\n", + " │ │ \"user_name\": \"Matthew Logan\", │ │\n", + " │ │ \"num_orders\": 20 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 4, │ │\n", - " │ │ \"user_name\": \"Rachelle Gonzalez\", │ │\n", - " │ │ \"num_orders\": 1 │ │\n", + " │ │ \"user_name\": \"Parisa Rafahari\", │ │\n", + " │ │ \"num_orders\": 4 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 5, │ │\n", - " │ │ \"user_name\": \"Frank Anderson\", │ │\n", - " │ │ \"num_orders\": 3 │ │\n", + " │ │ \"user_name\": \"John Mcdonald\", │ │\n", + " │ │ \"num_orders\": 6 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 6, │ │\n", - " │ │ \"user_name\": \"Lisa Taylor\", │ │\n", - " │ │ \"num_orders\": 5 │ │\n", + " │ │ \"user_name\": \"Micheal Anderson\", │ │\n", + " │ │ \"num_orders\": 2 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 7, │ │\n", - " │ │ \"user_name\": \"Peter Wilson\", │ │\n", - " │ │ \"num_orders\": 7 │ │\n", + " │ │ \"user_name\": \"Hannah Wilson\", │ │\n", + " │ │ \"num_orders\": 10 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"user_id\": 8, │ │\n", - " │ │ \"user_name\": \"Micheal Harris\", │ │\n", - " │ │ \"num_orders\": 4 │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"user_id\": 9, │ │\n", - " │ │ \"user_name\": \"Sarah Anderson\", │ │\n", - " │ │ \"num_orders\": 2 │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"user_id\": 10, │ │\n", - " │ │ \"user_name\": \"Jessica Taylor\", │ │\n", - " │ │ \"num_orders\": 1 │ │\n", + " │ │ \"user_name\": \"Jessica Wilson\", │ │\n", + " │ │ \"num_orders\": 18 │ │\n", " │ │ } │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", @@ -300,16 +300,16 @@ " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", " │ │ 'user_orders': [ │ │\n", - " │ │ {'user_id': 1, 'user_name': 'John Mcdonald', 'num_orders': 6}, │ │\n", - " │ │ {'user_id': 2, 'user_name': 'Jane Smith', 'num_orders': 4}, │ │\n", - " │ │ {'user_id': 3, 'user_name': 'David Lee', 'num_orders': 2}, │ │\n", - " │ │ {'user_id': 4, 'user_name': 'Rachelle Gonzalez', 'num_orders': 1}, │ │\n", - " │ │ {'user_id': 5, 'user_name': 'Frank Anderson', 'num_orders': 3}, │ │\n", - " │ │ {'user_id': 6, 'user_name': 'Lisa Taylor', 'num_orders': 5}, │ │\n", - " │ │ {'user_id': 7, 'user_name': 'Peter Wilson', 'num_orders': 7}, │ │\n", - " │ │ {'user_id': 8, 'user_name': 'Micheal Harris', 'num_orders': 4}, │ │\n", - " │ │ {'user_id': 9, 'user_name': 'Sarah Anderson', 'num_orders': 2}, │ │\n", - " │ │ {'user_id': 10, 'user_name': 'Jessica Taylor', 'num_orders': 1} │ │\n", + " │ │ {'user_id': 1, 'user_name': 'Fiona Gupta', 'num_orders': 8}, │ │\n", + " │ │ {'user_id': 2, 'user_name': 'Roger Mcdonald', 'num_orders': 10}, │ │\n", + " │ │ {'user_id': 3, 'user_name': 'Matthew Logan', 'num_orders': 20}, │ │\n", + " │ │ {'user_id': 4, 'user_name': 'Parisa Rafahari', 'num_orders': 4}, │ │\n", + " │ │ {'user_id': 5, 'user_name': 'John Mcdonald', 'num_orders': 6}, │ │\n", + " │ │ {'user_id': 6, 'user_name': 'Micheal Anderson', 'num_orders': 2}, │ │\n", + " │ │ {'user_id': 7, 'user_name': 'Hannah Wilson', 'num_orders': 10}, │ │\n", + " │ │ {'user_id': 8, 'user_name': 'Jessica Wilson', 'num_orders': 18}, │ │\n", + " │ │ {'user_id': 8, 'user_name': 'Jessica Wilson', 'num_orders': 18}, │ │\n", + " │ │ {'user_id': 8, 'user_name': 'Jessica Wilson', 'num_orders': 18} │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -328,19 +328,18 @@ " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -375,53 +374,43 @@ " │ \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", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"user_id\": 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 \"user_name\": \"John Mcdonald\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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\": \"Fiona Gupta\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 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\": 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\": \"Roger Mcdonald\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"David Lee\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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\": \"Matthew Logan\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Rachelle Gonzalez\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 \"user_name\": \"Parisa Rafahari\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Frank Anderson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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\": \"John Mcdonald\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Lisa Taylor\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"user_name\": \"Micheal Anderson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Peter Wilson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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\": \"Hannah Wilson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Micheal Harris\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Sarah Anderson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Jessica Taylor\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 \"user_name\": \"Jessica Wilson\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 18\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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", @@ -429,16 +418,16 @@ " │ \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 'user_orders': [\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 1, 'user_name': 'John Mcdonald', 'num_orders': 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_id': 2, 'user_name': 'Jane Smith', 'num_orders': 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_id': 3, 'user_name': 'David Lee', 'num_orders': 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_id': 4, 'user_name': 'Rachelle Gonzalez', 'num_orders': 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 {'user_id': 5, 'user_name': 'Frank Anderson', 'num_orders': 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_id': 6, 'user_name': 'Lisa Taylor', '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 {'user_id': 7, 'user_name': 'Peter Wilson', 'num_orders': 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_id': 8, 'user_name': 'Micheal Harris', 'num_orders': 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_id': 9, 'user_name': 'Sarah Anderson', 'num_orders': 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_id': 10, 'user_name': 'Jessica Taylor', 'num_orders': 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 {'user_id': 1, 'user_name': 'Fiona Gupta', 'num_orders': 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_id': 2, 'user_name': 'Roger Mcdonald', '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 {'user_id': 3, 'user_name': 'Matthew Logan', '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_id': 4, 'user_name': 'Parisa Rafahari', 'num_orders': 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_id': 5, 'user_name': 'John Mcdonald', 'num_orders': 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_id': 6, 'user_name': 'Micheal Anderson', 'num_orders': 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_id': 7, 'user_name': 'Hannah Wilson', '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 {'user_id': 8, 'user_name': 'Jessica Wilson', 'num_orders': 18},\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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, 'user_name': 'Jessica Wilson', 'num_orders': 18},\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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, 'user_name': 'Jessica Wilson', 'num_orders': 18}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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", @@ -472,7 +461,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/guardrails_with_chat_models.ipynb b/docs/examples/guardrails_with_chat_models.ipynb index d26409b4e..406007a0b 100644 --- a/docs/examples/guardrails_with_chat_models.ipynb +++ b/docs/examples/guardrails_with_chat_models.ipynb @@ -20,7 +20,9 @@ "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mone_line...\u001b[0m\n", "✅Successfully installed guardrails/one_line!\n", "\n", - "\n" + "\n", + "Requirement already satisfied: pypdfium2 in /Users/calebcourier/Projects/gr-mono/guardrails/.venv/lib/python3.12/site-packages (4.30.0)\n", + "Note: you may need to restart the kernel to use updated packages.\n" ] } ], @@ -57,14 +59,14 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/pypdfium2/_helpers/textpage.py:80: UserWarning: get_text_range() call with default params will be implicitly redirected to get_text_bounded()\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/pypdfium2/_helpers/textpage.py:80: UserWarning: get_text_range() call with default params will be implicitly redirected to get_text_bounded()\n", " warnings.warn(\"get_text_range() call with default params will be implicitly redirected to get_text_bounded()\")\n" ] }, @@ -248,7 +250,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -302,23 +304,15 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validators/__init__.py:51: FutureWarning: \n", - " Importing validators from `guardrails.validators` is deprecated.\n", - " All validators are now available in the Guardrails Hub. Please install\n", - " and import them from the hub instead. All validators will be\n", - " removed from this module in the next major release.\n", - "\n", - " Install with: `guardrails hub install hub:///`\n", - " Import as: from guardrails.hub import `ValidatorName`\n", - " \n", - " warn(\n" + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" ] } ], @@ -331,7 +325,7 @@ "\n", "${document}\n", "\n", - "${gr.complete_json_suffix_v2}\n", + "${gr.complete_xml_suffix_v2}\n", "\"\"\"\n", "\n", "class Fee(BaseModel):\n", @@ -370,29 +364,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validatorsattr.py:307: UserWarning: Validator 1-indexed is not installed!\n", - " warnings.warn(f\"Validator {validator_name} is not installed!\")\n", - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validatorsattr.py:307: UserWarning: Validator hub is not installed!\n", - " warnings.warn(f\"Validator {validator_name} is not installed!\")\n", - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validator_base.py:410: FutureWarning: Accessing `OneLine` using\n", - "`from guardrails.validators import OneLine` is deprecated and\n", - "support will be removed after version 0.5.x. Please switch to the Guardrails Hub syntax:\n", - "`from guardrails.hub import OneLine` for future updates and support.\n", - "For additional details, please visit: https://hub.guardrailsai.com/validator/guardrails/one_line.\n", - "\n", - " warn(\n", - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/guardrails/validatorsattr.py:307: UserWarning: Validator percentage is not installed!\n", - " warnings.warn(f\"Validator {validator_name} is not installed!\")\n" - ] - } - ], + "outputs": [], "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] @@ -406,35 +380,49 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=CreditCardAgreement)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As we can see, a few formatters weren't supported. These formatters won't be enforced in the output, but this information can still be used to generate a prompt.\n", + "As we can see, a few formatters weren't supported. These formatters won't be enforced in the output, but this information can still be used to generate a prompt." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", "\n", - "We see the prompt that will be sent to the LLM. The `{document}` is substituted with the user provided value at runtime." + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={\"document\": content[:6000]},\n", + " max_tokens=2048,\n", + " temperature=0,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see in the prompt that was sent to the LLM, the `{document}` is substituted with the user provided value at runtime." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 10, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/var/folders/c8/jqt82fpx785dpwpp36ljkgm40000gn/T/ipykernel_82182/3983563700.py:1: DeprecationWarning: 'Guard.base_prompt' is deprecated and will be removed in versions 0.5.x and beyond. Use 'Guard.history.last.prompt' instead.\n", - " print(guard.base_prompt)\n" - ] - }, { "data": { "text/html": [ @@ -442,29 +430,137 @@ "Given the following document, answer the following questions. If the answer doesn't exist in the document, enter \n", "'None'.\n", "\n", - "${document}\n", + "2/25/23, 7:59 PM about:blank\n", + "about:blank 1/4\n", + "PRICING INFORMATION\n", + "INTEREST RATES AND INTEREST CHARGES\n", + "Purchase Annual\n", + "Percentage Rate (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\n", + "Rate.\n", + "a\n", + "My Chase Loan\n", + "SM APR 19.49%. This APR will vary with the market based on the Prime Rate.\n", + "a\n", + "Promotional offers with fixed APRs and varying durations may be available from\n", + "time to time on some accounts.\n", + "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\n", + "Rate.\n", + "a\n", + "Cash Advance APR 29.49%. This APR will vary with the market based on the Prime Rate.\n", + "b\n", + "Penalty APR and When\n", + "It Applies\n", + "Up to 29.99%. This APR will vary with the market based on the Prime Rate.\n", + "c\n", + "We may apply the Penalty APR to your account if you:\n", + "fail to make a Minimum Payment by the date and time that it is due; or\n", + "make a payment to us that is returned unpaid.\n", + "How Long Will the Penalty APR Apply?: If we apply the Penalty APR for\n", + "either of these reasons, the Penalty APR could potentially remain in effect\n", + "indefinitely.\n", + "How to Avoid Paying\n", + "Interest on Purchases\n", + "Your due date will be a minimum of 21 days after the close of each billing cycle.\n", + "We will not charge you interest on new purchases if you pay your entire balance\n", + "or Interest Saving Balance by the due date each month. We will begin charging\n", + "interest on balance transfers and cash advances on the transaction date.\n", + "Minimum Interest\n", + "Charge\n", + "None\n", + "Credit Card Tips from\n", + "the Consumer Financial\n", + "Protection Bureau\n", + "To learn more about factors to consider when applying for or using a credit card,\n", + "visit the website of the Consumer Financial Protection Bureau at\n", + "http://www.consumerfinance.gov/learnmore.\n", + "FEES\n", + "Annual Membership\n", + "Fee\n", + "None\n", + "My Chase Plan\n", + "SM Fee\n", + "(fixed finance charge)\n", + "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\n", + "APR period.\n", + "After that, monthly fee of 1.72% of the amount of each eligible purchase\n", + "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\n", + "remain the same until the My Chase Plan is paid in full.\n", + "d\n", + "Transaction Fees\n", + "Balance Transfers Intro fee of 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%\n", + "of the amount of each transfer, whichever is greater.\n", + "Cash Advances Either $10 or 5% of the amount of each transaction, whichever is greater.\n", + "2/25/23, 7:59 PM about:blank\n", + "about:blank 2/4\n", + "Foreign Transactions 3% of the amount of each transaction in U.S. dollars.\n", + "Penalty Fees\n", + "Late Payment Up to $40.\n", + "Over-the-Credit-Limit None\n", + "Return Payment Up to $40.\n", + "Return Check None\n", + "Note: This account may not be eligible for balance transfers.\n", + "Loss of Intro APR: We will end your introductory APR if any required Minimum Payment is 60 days late, and\n", + "apply the Penalty APR.\n", + "How We Will Calculate Your Balance: We use the daily balance method (including new transactions).\n", + "Prime Rate: Variable APRs are based on the 7.75% Prime Rate as of 2/7/2023.\n", + "aWe add 11.74% to the Prime Rate to determine the Purchase/My Chase Loan/Balance Transfer APR.\n", + "Maximum APR 29.99%.\n", + "bWe add 21.74% to the Prime Rate to determine the Cash Advance APR. Maximum APR 29.99%.\n", + "cWe add up to 26.99% to the Prime Rate to determine the Penalty APR. Maximum APR 29.99%.\n", + "dMy Chase Plan Fee: The My Chase Plan Fee is calculated at the time each plan is created and is based on\n", + "the amount of each purchase transaction or amount selected to create the plan, the number of billing periods\n", + "you choose to pay the balance in full, and other factors. The monthly and aggregate dollar amount of your My\n", + "Chase Plan Fee will be disclosed during the activation of each My Chase Plan.\n", + "MILITARY LENDING ACT NOTICE: Federal law provides important protections to members of the Armed\n", + "Forces and their dependents relating to extensions of consumer credit. In general, the cost of consumer credit\n", + "to a member of the Armed Forces and his or her dependent may not exceed an annual percentage rate of 36\n", + "percent. This rate must include, as applicable to the credit transaction or account: the costs associated with\n", + "credit insurance premiums; fees for ancillary products sold in connection with the credit transaction; any\n", + "application fee charged (other than certain application fees for specified credit transactions or accounts); and\n", + "any participation fee charged (other than certain participation fees for a credit card account). To receive this\n", + "information and a description of your payment obligation verbally, please call 1-800-235-9978.\n", + "TERMS & CONDITIONS\n", + "Authorization: When you respond to this credit card offer from JPMorgan Chase Bank, N.A., Member FDIC, a\n", + "subsidiary of JPMorgan Chase & Co. (\"Chase\", \"we\", or \"us\"), you agree to the following:\n", + "1. You authorize us to obtain credit bureau reports, employment, and income information about you that we\n", + "will use when considering your application for credit. We may obtain and use information about your\n", + "accounts with us and others such as Checking, Deposit, Investment, and Utility accounts from credit\n", + "bureaus and other entities. You also authorize us to obtain credit bureau reports and any other\n", + "information about you in connection with: 1) extensions of credit on your account; 2) the administration,\n", + "review or collection of your account; and 3) offering you enhanced or additional products and services. If\n", + "you ask, we will tell you the name and address of the credit bureau from which we obtained a report\n", + "about you.\n", + "2. If an account is opened, you will receive a Cardmember Agreement with your card(s). You agree to the\n", + "terms of this agreement by: using the account or any card, authorizing their use, or making any payment\n", + "on the account.\n", + "3. By providing your mobile ph\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=\"fees\" description=\"What fees and charges are associated with my account?\">\n", - " <object>\n", - " <string name=\"name\" format=\"guardrails/lowercase; guardrails/two_words\"/>\n", - " <string name=\"explanation\" format=\"guardrails/one_line\"/>\n", - " <float name=\"value\" description=\"The fee amount in USD or as a percentage.\"/>\n", - " </object>\n", - " </list>\n", - " <list name=\"interest_rates\" description=\"What are the interest rates offered by the bank on different kinds of \n", - "accounts and products?\">\n", - " <object>\n", - " <string name=\"account_type\" format=\"guardrails/lowercase\"/>\n", - " <float name=\"rate\" description=\"The annual percentage rate (APR) for the account type.\"/>\n", - " </object>\n", - " </list>\n", + " <list description=\"What fees and charges are associated with my account?\" name=\"fees\" required=\"true\">\n", + " <object required=\"true\">\n", + " <string format=\"guardrails/lowercase; guardrails/two_words\" name=\"name\" required=\"true\"></string>\n", + " <string format=\"guardrails/one_line\" name=\"explanation\" required=\"true\"></string>\n", + " <float description=\"The fee amount in USD or as a percentage.\" name=\"value\" required=\"true\"></float>\n", + " </object>\n", + " </list>\n", + " <list description=\"What are the interest rates offered by the bank on different kinds of accounts and products?\" \n", + "name=\"interest_rates\" required=\"true\">\n", + " <object required=\"true\">\n", + " <string format=\"guardrails/lowercase\" name=\"account_type\" required=\"true\"></string>\n", + " <float description=\"The annual percentage rate (APR) for the account type.\" name=\"rate\" \n", + "required=\"true\"></float>\n", + " </object>\n", + " </list>\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", @@ -484,29 +580,137 @@ "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[1;36m2\u001b[0m/\u001b[1;36m25\u001b[0m/\u001b[1;36m23\u001b[0m, \u001b[1;92m7:59\u001b[0m PM about:blank\n", + "about:blank \u001b[1;36m1\u001b[0m/\u001b[1;36m4\u001b[0m\n", + "PRICING INFORMATION\n", + "INTEREST RATES AND INTEREST CHARGES\n", + "Purchase Annual\n", + "Percentage Rate \u001b[1m(\u001b[0mAPR\u001b[1m)\u001b[0m \u001b[1;36m0\u001b[0m% Intro APR for the first \u001b[1;36m18\u001b[0m months that your Account is open.\n", + "After that, \u001b[1;36m19.49\u001b[0m%. This APR will vary with the market based on the Prime\n", + "Rate.\n", + "a\n", + "My Chase Loan\n", + "SM APR \u001b[1;36m19.49\u001b[0m%. This APR will vary with the market based on the Prime Rate.\n", + "a\n", + "Promotional offers with fixed APRs and varying durations may be available from\n", + "time to time on some accounts.\n", + "Balance Transfer APR \u001b[1;36m0\u001b[0m% Intro APR for the first \u001b[1;36m18\u001b[0m months that your Account is open.\n", + "After that, \u001b[1;36m19.49\u001b[0m%. This APR will vary with the market based on the Prime\n", + "Rate.\n", + "a\n", + "Cash Advance APR \u001b[1;36m29.49\u001b[0m%. This APR will vary with the market based on the Prime Rate.\n", + "b\n", + "Penalty APR and When\n", + "It Applies\n", + "Up to \u001b[1;36m29.99\u001b[0m%. This APR will vary with the market based on the Prime Rate.\n", + "c\n", + "We may apply the Penalty APR to your account if you:\n", + "fail to make a Minimum Payment by the date and time that it is due; or\n", + "make a payment to us that is returned unpaid.\n", + "How Long Will the Penalty APR Apply?: If we apply the Penalty APR for\n", + "either of these reasons, the Penalty APR could potentially remain in effect\n", + "indefinitely.\n", + "How to Avoid Paying\n", + "Interest on Purchases\n", + "Your due date will be a minimum of \u001b[1;36m21\u001b[0m days after the close of each billing cycle.\n", + "We will not charge you interest on new purchases if you pay your entire balance\n", + "or Interest Saving Balance by the due date each month. We will begin charging\n", + "interest on balance transfers and cash advances on the transaction date.\n", + "Minimum Interest\n", + "Charge\n", + "\u001b[3;35mNone\u001b[0m\n", + "Credit Card Tips from\n", + "the Consumer Financial\n", + "Protection Bureau\n", + "To learn more about factors to consider when applying for or using a credit card,\n", + "visit the website of the Consumer Financial Protection Bureau at\n", + "\u001b[4;94mhttp://www.consumerfinance.gov/learnmore.\u001b[0m\n", + "FEES\n", + "Annual Membership\n", + "Fee\n", + "\u001b[3;35mNone\u001b[0m\n", + "My Chase Plan\n", + "SM Fee\n", + "\u001b[1m(\u001b[0mfixed finance charge\u001b[1m)\u001b[0m\n", + "Monthly fee of \u001b[1;36m0\u001b[0m% of the amount of each eligible purchase transaction or\n", + "amount selected to create a My Chase Plan while in the \u001b[1;36m0\u001b[0m% Intro Purchase\n", + "APR period.\n", + "After that, monthly fee of \u001b[1;36m1.72\u001b[0m% of the amount of each eligible purchase\n", + "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\n", + "remain the same until the My Chase Plan is paid in full.\n", + "d\n", + "Transaction Fees\n", + "Balance Transfers Intro fee of either $\u001b[1;36m5\u001b[0m or \u001b[1;36m3\u001b[0m% of the amount of each transfer, whichever is greater,\n", + "on transfers made within \u001b[1;36m60\u001b[0m days of account opening. After that: Either $\u001b[1;36m5\u001b[0m or \u001b[1;36m5\u001b[0m%\n", + "of the amount of each transfer, whichever is greater.\n", + "Cash Advances Either $\u001b[1;36m10\u001b[0m or \u001b[1;36m5\u001b[0m% of the amount of each transaction, whichever is greater.\n", + "\u001b[1;36m2\u001b[0m/\u001b[1;36m25\u001b[0m/\u001b[1;36m23\u001b[0m, \u001b[1;92m7:59\u001b[0m PM about:blank\n", + "about:blank \u001b[1;36m2\u001b[0m/\u001b[1;36m4\u001b[0m\n", + "Foreign Transactions \u001b[1;36m3\u001b[0m% of the amount of each transaction in U.S. dollars.\n", + "Penalty Fees\n", + "Late Payment Up to $\u001b[1;36m40\u001b[0m.\n", + "Over-the-Credit-Limit \u001b[3;35mNone\u001b[0m\n", + "Return Payment Up to $\u001b[1;36m40\u001b[0m.\n", + "Return Check \u001b[3;35mNone\u001b[0m\n", + "Note: This account may not be eligible for balance transfers.\n", + "Loss of Intro APR: We will end your introductory APR if any required Minimum Payment is \u001b[1;36m60\u001b[0m days late, and\n", + "apply the Penalty APR.\n", + "How We Will Calculate Your Balance: We use the daily balance method \u001b[1m(\u001b[0mincluding new transactions\u001b[1m)\u001b[0m.\n", + "Prime Rate: Variable APRs are based on the \u001b[1;36m7.75\u001b[0m% Prime Rate as of \u001b[1;36m2\u001b[0m/\u001b[1;36m7\u001b[0m/\u001b[1;36m2023\u001b[0m.\n", + "aWe add \u001b[1;36m11.74\u001b[0m% to the Prime Rate to determine the Purchase/My Chase Loan/Balance Transfer APR.\n", + "Maximum APR \u001b[1;36m29.99\u001b[0m%.\n", + "bWe add \u001b[1;36m21.74\u001b[0m% to the Prime Rate to determine the Cash Advance APR. Maximum APR \u001b[1;36m29.99\u001b[0m%.\n", + "cWe add up to \u001b[1;36m26.99\u001b[0m% to the Prime Rate to determine the Penalty APR. Maximum APR \u001b[1;36m29.99\u001b[0m%.\n", + "dMy Chase Plan Fee: The My Chase Plan Fee is calculated at the time each plan is created and is based on\n", + "the amount of each purchase transaction or amount selected to create the plan, the number of billing periods\n", + "you choose to pay the balance in full, and other factors. The monthly and aggregate dollar amount of your My\n", + "Chase Plan Fee will be disclosed during the activation of each My Chase Plan.\n", + "MILITARY LENDING ACT NOTICE: Federal law provides important protections to members of the Armed\n", + "Forces and their dependents relating to extensions of consumer credit. In general, the cost of consumer credit\n", + "to a member of the Armed Forces and his or her dependent may not exceed an annual percentage rate of \u001b[1;36m36\u001b[0m\n", + "percent. This rate must include, as applicable to the credit transaction or account: the costs associated with\n", + "credit insurance premiums; fees for ancillary products sold in connection with the credit transaction; any\n", + "application fee charged \u001b[1m(\u001b[0mother than certain application fees for specified credit transactions or accounts\u001b[1m)\u001b[0m; and\n", + "any participation fee charged \u001b[1m(\u001b[0mother than certain participation fees for a credit card account\u001b[1m)\u001b[0m. To receive this\n", + "information and a description of your payment obligation verbally, please call \u001b[1;36m1\u001b[0m-\u001b[1;36m800\u001b[0m-\u001b[1;36m235\u001b[0m-\u001b[1;36m9978\u001b[0m.\n", + "TERMS & CONDITIONS\n", + "Authorization: When you respond to this credit card offer from JPMorgan Chase Bank, N.A., Member FDIC, a\n", + "subsidiary of JPMorgan Chase & Co. \u001b[1m(\u001b[0m\u001b[32m\"Chase\"\u001b[0m, \u001b[32m\"we\"\u001b[0m, or \u001b[32m\"us\"\u001b[0m\u001b[1m)\u001b[0m, you agree to the following:\n", + "\u001b[1;36m1\u001b[0m. You authorize us to obtain credit bureau reports, employment, and income information about you that we\n", + "will use when considering your application for credit. We may obtain and use information about your\n", + "accounts with us and others such as Checking, Deposit, Investment, and Utility accounts from credit\n", + "bureaus and other entities. You also authorize us to obtain credit bureau reports and any other\n", + "information about you in connection with: \u001b[1;36m1\u001b[0m\u001b[1m)\u001b[0m extensions of credit on your account; \u001b[1;36m2\u001b[0m\u001b[1m)\u001b[0m the administration,\n", + "review or collection of your account; and \u001b[1;36m3\u001b[0m\u001b[1m)\u001b[0m offering you enhanced or additional products and services. If\n", + "you ask, we will tell you the name and address of the credit bureau from which we obtained a report\n", + "about you.\n", + "\u001b[1;36m2\u001b[0m. If an account is opened, you will receive a Cardmember Agreement with your \u001b[1;35mcard\u001b[0m\u001b[1m(\u001b[0ms\u001b[1m)\u001b[0m. You agree to the\n", + "terms of this agreement by: using the account or any card, authorizing their use, or making any payment\n", + "on the account.\n", + "\u001b[1;36m3\u001b[0m. By providing your mobile ph\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\u001b[35m/\u001b[0m\u001b[95mlist\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\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", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mfloat\u001b[0m\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", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mfloat\u001b[0m\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", "\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", @@ -526,31 +730,7 @@ } ], "source": [ - "print(guard.base_prompt)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here's the formatted instructions sent as the system message to the LLM." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={\"document\": content[:6000]},\n", - " max_tokens=2048,\n", - " temperature=0,\n", - ")" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -564,7 +744,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -572,7 +752,7 @@ "text/html": [ "
{\n",
        "    'fees': [\n",
-       "        {'name': 'annual membership', 'explanation': 'None', 'value': 0.0},\n",
+       "        {'name': 'annual membership', 'explanation': 'None', 'value': 0},\n",
        "        {\n",
        "            'name': 'my chase',\n",
        "            'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction or amount \n",
@@ -580,7 +760,7 @@
        "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': 0.0\n",
+       "            'value': 0\n",
        "        },\n",
        "        {\n",
        "            'name': 'transaction fees',\n",
@@ -588,24 +768,26 @@
        "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. Cash Advances: Either $10 or 5% of the amount of each transaction, \n",
        "whichever is greater.',\n",
-       "            'value': 0.0\n",
+       "            'value': 0\n",
        "        },\n",
        "        {\n",
        "            'name': 'foreign transactions',\n",
        "            'explanation': '3% of the amount of each transaction in U.S. dollars',\n",
-       "            'value': 0.0\n",
+       "            'value': 0\n",
        "        },\n",
        "        {\n",
        "            'name': 'penalty fees',\n",
        "            'explanation': 'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. Return\n",
        "Check: None',\n",
-       "            'value': 0.0\n",
+       "            'value': 0\n",
        "        }\n",
        "    ],\n",
        "    'interest_rates': [\n",
-       "        {'account_type': 'purchase/my chase loan/balance transfer', 'rate': 19.49},\n",
-       "        {'account_type': 'cash advance', 'rate': 29.49},\n",
-       "        {'account_type': 'penalty', 'rate': 29.99}\n",
+       "        {'account_type': 'purchase annual percentage rate (apr)', 'rate': 0},\n",
+       "        {'account_type': 'my chase loan apr', 'rate': 19.49},\n",
+       "        {'account_type': 'balance transfer apr', 'rate': 0},\n",
+       "        {'account_type': 'cash advance apr', 'rate': 29.49},\n",
+       "        {'account_type': 'penalty apr', 'rate': 29.99}\n",
        "    ]\n",
        "}\n",
        "
\n" @@ -613,7 +795,7 @@ "text/plain": [ "\u001b[1m{\u001b[0m\n", " \u001b[32m'fees'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1m{\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\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\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\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", @@ -621,7 +803,7 @@ "\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;36m0.0\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'transaction fees'\u001b[0m,\n", @@ -629,24 +811,26 @@ "\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. Cash Advances: Either $10 or 5% of the amount of each transaction, \u001b[0m\n", "\u001b[32mwhichever is greater.'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.0\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\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;36m0.0\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'penalty fees'\u001b[0m,\n", " \u001b[32m'explanation'\u001b[0m: \u001b[32m'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. Return\u001b[0m\n", "\u001b[32mCheck: None'\u001b[0m,\n", - " \u001b[32m'value'\u001b[0m: \u001b[1;36m0.0\u001b[0m\n", + " \u001b[32m'value'\u001b[0m: \u001b[1;36m0\u001b[0m\n", " \u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m,\n", " \u001b[32m'interest_rates'\u001b[0m: \u001b[1m[\u001b[0m\n", - " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'purchase/my chase loan/balance transfer'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'cash advance'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.49\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'penalty'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.99\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \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[32m'rate'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'my chase loan apr'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m19.49\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'balance transfer apr'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m0\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'cash advance apr'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.49\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'account_type'\u001b[0m: \u001b[32m'penalty apr'\u001b[0m, \u001b[32m'rate'\u001b[0m: \u001b[1;36m29.99\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -661,7 +845,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -802,23 +986,25 @@ "│ │ │ it into. │ │\n", "│ │ │ │ │\n", "│ │ │ <output> │ │\n", - "│ │ │ <list name=\"fees\" description=\"What fees and charges are associated with my account?\"> │ │\n", - "│ │ │ <object> │ │\n", - "│ │ │ <string name=\"name\" format=\"guardrails/lowercase; guardrails/two_words\"/> │ │\n", - "│ │ │ <string name=\"explanation\" format=\"guardrails/one_line\"/> │ │\n", - "│ │ │ <float name=\"value\" description=\"The fee amount in USD or as a percentage.\"/> │ │\n", - "│ │ │ </object> │ │\n", - "│ │ │ </list> │ │\n", - "│ │ │ <list name=\"interest_rates\" description=\"What are the interest rates offered by the bank on │ │\n", - "│ │ │ different kinds of accounts and products?\"> │ │\n", - "│ │ │ <object> │ │\n", - "│ │ │ <string name=\"account_type\" format=\"guardrails/lowercase\"/> │ │\n", - "│ │ │ <float name=\"rate\" description=\"The annual percentage rate (APR) for the account type.\"/> │ │\n", - "│ │ │ </object> │ │\n", - "│ │ │ </list> │ │\n", + "│ │ │ <list description=\"What fees and charges are associated with my account?\" name=\"fees\" │ │\n", + "│ │ │ required=\"true\"> │ │\n", + "│ │ │ <object required=\"true\"> │ │\n", + "│ │ │ <string format=\"guardrails/lowercase; guardrails/two_words\" name=\"name\" required=\"true\"></string> │ │\n", + "│ │ │ <string format=\"guardrails/one_line\" name=\"explanation\" required=\"true\"></string> │ │\n", + "│ │ │ <float description=\"The fee amount in USD or as a percentage.\" name=\"value\" │ │\n", + "│ │ │ required=\"true\"></float> │ │\n", + "│ │ │ </object> │ │\n", + "│ │ │ </list> │ │\n", + "│ │ │ <list description=\"What are the interest rates offered by the bank on different kinds of accounts and │ │\n", + "│ │ │ products?\" name=\"interest_rates\" required=\"true\"> │ │\n", + "│ │ │ <object required=\"true\"> │ │\n", + "│ │ │ <string format=\"guardrails/lowercase\" name=\"account_type\" required=\"true\"></string> │ │\n", + "│ │ │ <float description=\"The annual percentage rate (APR) for the account type.\" name=\"rate\" │ │\n", + "│ │ │ required=\"true\"></float> │ │\n", + "│ │ │ </object> │ │\n", + "│ │ │ </list> │ │\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 │ │\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", @@ -841,71 +1027,55 @@ "│ │ │ No message history. │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", "│ │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"fees\": [ │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"name\": \"annual membership\", │ │\n", - "│ │ │ \"explanation\": \"None\", │ │\n", - "│ │ │ \"value\": 0 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"name\": \"my chase plan 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\": 0 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"name\": \"transaction fees\", │ │\n", - "│ │ │ \"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. Cash Advances: Either $10 or 5% of the │ │\n", - "│ │ │ amount of each transaction, whichever is greater.\", │ │\n", - "│ │ │ \"value\": 0 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"name\": \"foreign transactions\", │ │\n", - "│ │ │ \"explanation\": \"3% of the amount of each transaction in U.S. dollars\", │ │\n", - "│ │ │ \"value\": 0 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"name\": \"penalty fees\", │ │\n", - "│ │ │ \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to │ │\n", - "│ │ │ $40. Return Check: None\", │ │\n", - "│ │ │ \"value\": 0 │ │\n", - "│ │ │ } │ │\n", - "│ │ │ ], │ │\n", - "│ │ │ \"interest_rates\": [ │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"account_type\": \"purchase/my chase loan/balance transfer\", │ │\n", - "│ │ │ \"rate\": 19.49 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"account_type\": \"cash advance\", │ │\n", - "│ │ │ \"rate\": 29.49 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"account_type\": \"penalty\", │ │\n", - "│ │ │ \"rate\": 29.99 │ │\n", - "│ │ │ } │ │\n", - "│ │ │ ] │ │\n", - "│ │ │ } │ │\n", + "│ │ │ {\"fees\":[{\"name\":\"annual membership fee\",\"explanation\":\"None\",\"value\":0},{\"name\":\"my chase plan │ │\n", + "│ │ │ fee\",\"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.\",\"value\":0},{\"name\":\"transaction │ │\n", + "│ │ │ fees\",\"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. Cash Advances: Either $10 or 5% of the amount of │ │\n", + "│ │ │ each transaction, whichever is greater.\",\"value\":0},{\"name\":\"foreign transactions\",\"explanation\":\"3% of │ │\n", + "│ │ │ the amount of each transaction in U.S. dollars\",\"value\":0},{\"name\":\"penalty fees\",\"explanation\":\"Late │ │\n", + "│ │ │ Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. Return Check: │ │\n", + "│ │ │ None\",\"value\":0}],\"interest_rates\":[{\"account_type\":\"purchase annual percentage rate │ │\n", + "│ │ │ (apr)\",\"rate\":0},{\"account_type\":\"my chase loan apr\",\"rate\":19.49},{\"account_type\":\"balance transfer │ │\n", + "│ │ │ apr\",\"rate\":0},{\"account_type\":\"cash advance apr\",\"rate\":29.49},{\"account_type\":\"penalty │ │\n", + "│ │ │ apr\",\"rate\":29.99}]} │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", "│ │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", "│ │ │ { │ │\n", "│ │ │ 'fees': [ │ │\n", - "│ │ │ {'name': 'annual membership', 'explanation': 'None', 'value': 0.0}, │ │\n", "│ │ │ { │ │\n", "│ │ │ 'name': FieldReAsk( │ │\n", - "│ │ │ incorrect_value='my chase plan fee', │ │\n", + "│ │ │ incorrect_value='annual membership fee', │ │\n", "│ │ │ fail_results=[ │ │\n", "│ │ │ FailResult( │ │\n", "│ │ │ outcome='fail', │ │\n", + "│ │ │ error_message='Value must be exactly two words', │ │\n", + "│ │ │ fix_value='annual membership', │ │\n", "│ │ │ metadata=None, │ │\n", + "│ │ │ validated_chunk=None, │ │\n", + "│ │ │ error_spans=None │ │\n", + "│ │ │ ) │ │\n", + "│ │ │ ], │ │\n", + "│ │ │ path=['fees', 0, 'name'] │ │\n", + "│ │ │ ), │ │\n", + "│ │ │ 'explanation': 'None', │ │\n", + "│ │ │ 'value': 0 │ │\n", + "│ │ │ }, │ │\n", + "│ │ │ { │ │\n", + "│ │ │ 'name': FieldReAsk( │ │\n", + "│ │ │ incorrect_value='my chase plan fee', │ │\n", + "│ │ │ fail_results=[ │ │\n", + "│ │ │ FailResult( │ │\n", + "│ │ │ outcome='fail', │ │\n", "│ │ │ error_message='Value must be exactly two words', │ │\n", - "│ │ │ fix_value='my chase' │ │\n", + "│ │ │ fix_value='my chase', │ │\n", + "│ │ │ metadata=None, │ │\n", + "│ │ │ validated_chunk=None, │ │\n", + "│ │ │ error_spans=None │ │\n", "│ │ │ ) │ │\n", "│ │ │ ], │ │\n", "│ │ │ path=['fees', 1, 'name'] │ │\n", @@ -915,7 +1085,7 @@ "│ │ │ 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': 0.0 │ │\n", + "│ │ │ 'value': 0 │ │\n", "│ │ │ }, │ │\n", "│ │ │ { │ │\n", "│ │ │ 'name': 'transaction fees', │ │\n", @@ -923,27 +1093,26 @@ "│ │ │ 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. Cash Advances: Either $10 or 5% of the │ │\n", "│ │ │ amount of each transaction, whichever is greater.', │ │\n", - "│ │ │ 'value': 0.0 │ │\n", + "│ │ │ 'value': 0 │ │\n", "│ │ │ }, │ │\n", "│ │ │ { │ │\n", "│ │ │ 'name': 'foreign transactions', │ │\n", "│ │ │ 'explanation': '3% of the amount of each transaction in U.S. dollars', │ │\n", - "│ │ │ 'value': 0.0 │ │\n", + "│ │ │ 'value': 0 │ │\n", "│ │ │ }, │ │\n", "│ │ │ { │ │\n", "│ │ │ 'name': 'penalty fees', │ │\n", "│ │ │ 'explanation': 'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to │ │\n", "│ │ │ $40. Return Check: None', │ │\n", - "│ │ │ 'value': 0.0 │ │\n", + "│ │ │ 'value': 0 │ │\n", "│ │ │ } │ │\n", "│ │ │ ], │ │\n", "│ │ │ 'interest_rates': [ │ │\n", - "│ │ │ { │ │\n", - "│ │ │ 'account_type': 'purchase/my chase loan/balance transfer', │ │\n", - "│ │ │ 'rate': 19.49 │ │\n", - "│ │ │ }, │ │\n", - "│ │ │ {'account_type': 'cash advance', 'rate': 29.49}, │ │\n", - "│ │ │ {'account_type': 'penalty', 'rate': 29.99} │ │\n", + "│ │ │ {'account_type': 'purchase annual percentage rate (apr)', 'rate': 0}, │ │\n", + "│ │ │ {'account_type': 'my chase loan apr', 'rate': 19.49}, │ │\n", + "│ │ │ {'account_type': 'balance transfer apr', 'rate': 0}, │ │\n", + "│ │ │ {'account_type': 'cash advance apr', 'rate': 29.49}, │ │\n", + "│ │ │ {'account_type': 'penalty apr', 'rate': 29.99} │ │\n", "│ │ │ ] │ │\n", "│ │ │ } │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -956,9 +1125,14 @@ " │ │ { │ │\n", " │ │ \"fees\": [ │ │\n", " │ │ { │ │\n", - " │ │ \"name\": \"annual membership\", │ │\n", + " │ │ \"name\": { │ │\n", + " │ │ \"incorrect_value\": \"annual membership fee\", │ │\n", + " │ │ \"error_messages\": [ │ │\n", + " │ │ \"Value must be exactly two words\" │ │\n", + " │ │ ] │ │\n", + " │ │ }, │ │\n", " │ │ \"explanation\": \"None\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": { │ │\n", @@ -972,7 +1146,7 @@ " │ │ 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\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"transaction fees\", │ │\n", @@ -980,31 +1154,39 @@ " │ │ 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. Cash Advances: Either $10 or 5% of the amount of │ │\n", " │ │ each transaction, whichever is greater.\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"foreign transactions\", │ │\n", " │ │ \"explanation\": \"3% of the amount of each transaction in U.S. dollars\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"penalty fees\", │ │\n", " │ │ \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. │ │\n", " │ │ Return Check: None\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ } │ │\n", " │ │ ], │ │\n", " │ │ \"interest_rates\": [ │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"purchase/my chase loan/balance transfer\", │ │\n", + " │ │ \"account_type\": \"purchase annual percentage rate (apr)\", │ │\n", + " │ │ \"rate\": 0 │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"account_type\": \"my chase loan apr\", │ │\n", " │ │ \"rate\": 19.49 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"cash advance\", │ │\n", + " │ │ \"account_type\": \"balance transfer apr\", │ │\n", + " │ │ \"rate\": 0 │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"account_type\": \"cash advance apr\", │ │\n", " │ │ \"rate\": 29.49 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"penalty\", │ │\n", + " │ │ \"account_type\": \"penalty apr\", │ │\n", " │ │ \"rate\": 29.99 │ │\n", " │ │ } │ │\n", " │ │ ] │ │\n", @@ -1016,23 +1198,25 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <list name=\"fees\" description=\"What fees and charges are associated with my account?\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <string name=\"name\" format=\"guardrails/lowercase; guardrails/two_words\"/> │ │\n", - " │ │ <string name=\"explanation\" format=\"guardrails/one_line\"/> │ │\n", - " │ │ <float name=\"value\" description=\"The fee amount in USD or as a percentage.\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", - " │ │ <list name=\"interest_rates\" description=\"What are the interest rates offered by the bank on │ │\n", - " │ │ different kinds of accounts and products?\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <string name=\"account_type\" format=\"guardrails/lowercase\"/> │ │\n", - " │ │ <float name=\"rate\" description=\"The annual percentage rate (APR) for the account type.\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", + " │ │ <list description=\"What fees and charges are associated with my account?\" name=\"fees\" │ │\n", + " │ │ required=\"true\"> │ │\n", + " │ │ <object required=\"true\"> │ │\n", + " │ │ <string format=\"guardrails/lowercase; guardrails/two_words\" name=\"name\" required=\"true\"></string> │ │\n", + " │ │ <string format=\"guardrails/one_line\" name=\"explanation\" required=\"true\"></string> │ │\n", + " │ │ <float description=\"The fee amount in USD or as a percentage.\" name=\"value\" │ │\n", + " │ │ required=\"true\"></float> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\n", + " │ │ <list description=\"What are the interest rates offered by the bank on different kinds of accounts and │ │\n", + " │ │ products?\" name=\"interest_rates\" required=\"true\"> │ │\n", + " │ │ <object required=\"true\"> │ │\n", + " │ │ <string format=\"guardrails/lowercase\" name=\"account_type\" required=\"true\"></string> │ │\n", + " │ │ <float description=\"The annual percentage rate (APR) for the account type.\" name=\"rate\" │ │\n", + " │ │ required=\"true\"></float> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\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 │ │\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", @@ -1053,16 +1237,16 @@ " │ │ { │ │\n", " │ │ \"name\": \"annual membership\", │ │\n", " │ │ \"explanation\": \"None\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", - " │ │ \"name\": \"my chase plan fee\", │ │\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 │ │\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\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"transaction fees\", │ │\n", @@ -1070,31 +1254,39 @@ " │ │ 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. Cash Advances: Either $10 or 5% of the amount of │ │\n", " │ │ each transaction, whichever is greater.\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"foreign transactions\", │ │\n", " │ │ \"explanation\": \"3% of the amount of each transaction in U.S. dollars\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ \"name\": \"penalty fees\", │ │\n", " │ │ \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. │ │\n", " │ │ Return Check: None\", │ │\n", - " │ │ \"value\": 0.0 │ │\n", + " │ │ \"value\": 0 │ │\n", " │ │ } │ │\n", " │ │ ], │ │\n", " │ │ \"interest_rates\": [ │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"purchase/my chase loan/balance transfer\", │ │\n", + " │ │ \"account_type\": \"purchase annual percentage rate (apr)\", │ │\n", + " │ │ \"rate\": 0 │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"account_type\": \"my chase loan apr\", │ │\n", " │ │ \"rate\": 19.49 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"cash advance\", │ │\n", + " │ │ \"account_type\": \"balance transfer apr\", │ │\n", + " │ │ \"rate\": 0 │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"account_type\": \"cash advance apr\", │ │\n", " │ │ \"rate\": 29.49 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", - " │ │ \"account_type\": \"penalty\", │ │\n", + " │ │ \"account_type\": \"penalty apr\", │ │\n", " │ │ \"rate\": 29.99 │ │\n", " │ │ } │ │\n", " │ │ ] │ │\n", @@ -1103,7 +1295,7 @@ " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", " │ │ 'fees': [ │ │\n", - " │ │ {'name': 'annual membership', 'explanation': 'None', 'value': 0.0}, │ │\n", + " │ │ {'name': 'annual membership', 'explanation': 'None', 'value': 0}, │ │\n", " │ │ { │ │\n", " │ │ 'name': 'my chase', │ │\n", " │ │ 'explanation': 'Monthly fee of 0% of the amount of each eligible purchase transaction or │ │\n", @@ -1111,7 +1303,7 @@ " │ │ 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': 0.0 │ │\n", + " │ │ 'value': 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'name': 'transaction fees', │ │\n", @@ -1119,27 +1311,26 @@ " │ │ 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. Cash Advances: Either $10 or 5% of the │ │\n", " │ │ amount of each transaction, whichever is greater.', │ │\n", - " │ │ 'value': 0.0 │ │\n", + " │ │ 'value': 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'name': 'foreign transactions', │ │\n", " │ │ 'explanation': '3% of the amount of each transaction in U.S. dollars', │ │\n", - " │ │ 'value': 0.0 │ │\n", + " │ │ 'value': 0 │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'name': 'penalty fees', │ │\n", " │ │ 'explanation': 'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to │ │\n", " │ │ $40. Return Check: None', │ │\n", - " │ │ 'value': 0.0 │ │\n", + " │ │ 'value': 0 │ │\n", " │ │ } │ │\n", " │ │ ], │ │\n", " │ │ 'interest_rates': [ │ │\n", - " │ │ { │ │\n", - " │ │ 'account_type': 'purchase/my chase loan/balance transfer', │ │\n", - " │ │ 'rate': 19.49 │ │\n", - " │ │ }, │ │\n", - " │ │ {'account_type': 'cash advance', 'rate': 29.49}, │ │\n", - " │ │ {'account_type': 'penalty', 'rate': 29.99} │ │\n", + " │ │ {'account_type': 'purchase annual percentage rate (apr)', 'rate': 0}, │ │\n", + " │ │ {'account_type': 'my chase loan apr', 'rate': 19.49}, │ │\n", + " │ │ {'account_type': 'balance transfer apr', 'rate': 0}, │ │\n", + " │ │ {'account_type': 'cash advance apr', 'rate': 29.49}, │ │\n", + " │ │ {'account_type': 'penalty apr', 'rate': 29.99} │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -1282,23 +1473,25 @@ "│ │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1321,71 +1514,55 @@ "│ │ \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 \"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 \"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 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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\": 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\": \"transaction 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 \"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. Cash Advances: Either $10 or 5% 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;220mamount 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\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"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\": 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\": \"penalty 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 \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m$40. 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 \"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 {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"account_type\": \"purchase/my chase loan/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 \"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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"account_type\": \"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 \"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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"account_type\": \"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 \"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 }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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{\"fees\":[{\"name\":\"annual membership fee\",\"explanation\":\"None\",\"value\":0},{\"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;220mfee\",\"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.\",\"value\":0},{\"name\":\"transaction \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mfees\",\"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. Cash Advances: Either $10 or 5% of the amount of \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220meach transaction, whichever is greater.\",\"value\":0},{\"name\":\"foreign transactions\",\"explanation\":\"3% of\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 amount of each transaction in U.S. dollars\",\"value\":0},{\"name\":\"penalty fees\",\"explanation\":\"Late \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mPayment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. 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;220mNone\",\"value\":0}],\"interest_rates\":[{\"account_type\":\"purchase annual percentage 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(apr)\",\"rate\":0},{\"account_type\":\"my chase loan apr\",\"rate\":19.49},{\"account_type\":\"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;220mapr\",\"rate\":0},{\"account_type\":\"cash advance apr\",\"rate\":29.49},{\"account_type\":\"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;220mapr\",\"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 │\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 {'name': 'annual membership', 'explanation': 'None', '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 '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 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 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 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 error_message='Value 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 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 validated_chunk=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_spans=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=['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\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 '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 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 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 error_message='Value 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 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 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 validated_chunk=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_spans=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=['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", @@ -1395,7 +1572,7 @@ "│ │ \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': 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 '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", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'transaction 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", @@ -1403,27 +1580,26 @@ "│ │ \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. Cash Advances: Either $10 or 5% 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;240mamount of each transaction, 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': 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 '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", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 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 '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", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'penalty 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 'explanation': 'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m$40. Return Check: 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 '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", "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'account_type': 'purchase/my chase loan/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 '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 },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'account_type': 'cash advance', '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 {'account_type': 'penalty', '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 {'account_type': 'purchase annual percentage rate (apr)', 'rate': 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 {'account_type': 'my chase loan apr', '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 {'account_type': 'balance transfer apr', 'rate': 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 {'account_type': 'cash advance apr', '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 {'account_type': 'penalty apr', '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 ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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", @@ -1436,9 +1612,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 \"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\": \"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 \"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_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 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\": \"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 \"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 \"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", @@ -1452,7 +1633,7 @@ " │ \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\": 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 \"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 \"name\": \"transaction 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", @@ -1460,31 +1641,39 @@ " │ \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. Cash Advances: Either $10 or 5% of the amount of \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255meach 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.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 \"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 \"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\": 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 \"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 \"name\": \"penalty 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 \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mReturn 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.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 \"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 {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"account_type\": \"purchase/my chase loan/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 \"account_type\": \"purchase annual percentage rate (apr)\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"rate\": 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 \"account_type\": \"my chase loan apr\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"rate\": 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 },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"account_type\": \"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 \"account_type\": \"balance transfer apr\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"rate\": 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 \"account_type\": \"cash advance apr\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"rate\": 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 },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m {\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"account_type\": \"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 \"account_type\": \"penalty apr\",\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"rate\": 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 }\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1496,23 +1685,25 @@ " │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1533,16 +1724,16 @@ " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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 \"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\": \"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 \"name\": \"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;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\": 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 \"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\": \"transaction 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", @@ -1550,31 +1741,39 @@ " │ \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. Cash Advances: Either $10 or 5% of the amount of \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220meach 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.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 \"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\": \"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\": 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 \"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\": \"penalty 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 \"explanation\": \"Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to $40. \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mReturn 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.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 \"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 \"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 {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"account_type\": \"purchase/my chase loan/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 \"account_type\": \"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 \"rate\": 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 \"account_type\": \"my chase loan 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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"account_type\": \"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 \"account_type\": \"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 \"rate\": 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 \"account_type\": \"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 },\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"account_type\": \"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 \"account_type\": \"penalty 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 }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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", @@ -1583,7 +1782,7 @@ " │ \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 {'name': 'annual membership', 'explanation': 'None', '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 {'name': 'annual membership', 'explanation': 'None', '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", " │ \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", @@ -1591,7 +1790,7 @@ " │ \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': 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 '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", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'transaction 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", @@ -1599,34 +1798,33 @@ " │ \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. Cash Advances: Either $10 or 5% 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;240mamount of each transaction, 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': 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 '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", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 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 '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", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'penalty 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 'explanation': 'Late Payment: Up to $40. Over-the-Credit-Limit: None. Return Payment: Up to\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m$40. Return Check: 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 '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", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m ],\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m 'account_type': 'purchase/my chase loan/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 '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 },\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {'account_type': 'cash advance', '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 {'account_type': 'penalty', '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 {'account_type': 'purchase annual percentage rate (apr)', 'rate': 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 {'account_type': 'my chase loan apr', '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 {'account_type': 'balance transfer apr', 'rate': 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 {'account_type': 'cash advance apr', '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 {'account_type': 'penalty apr', '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 ]\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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": 10, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -1652,7 +1850,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/examples/input_validation.ipynb b/docs/examples/input_validation.ipynb index 3018c2c07..6b5ae7af2 100644 --- a/docs/examples/input_validation.ipynb +++ b/docs/examples/input_validation.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -38,11 +38,20 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": { "is_executing": true }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ "from guardrails import Guard\n", "\n", @@ -73,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": { "is_executing": true }, @@ -99,19 +108,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/zaydsimjee/workspace/guardrails/docs/examples/.venv/lib/python3.10/site-packages/IPython/core/interactiveshell.py:3577: FutureWarning: The `with_prompt_validation` method is deprecated,\n", - " and will be removed in 0.5.x. Instead, please use\n", - " `Guard().use(YourValidator, on='prompt')`.\n", - " exec(code_obj, self.user_global_ns, self.user_ns)\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -131,7 +130,7 @@ "\n", "\n", "guard = Guard.from_pydantic(Pet)\n", - "guard.with_prompt_validation([TwoWords(on_fail=\"exception\")])\n", + "guard.use(TwoWords(on_fail=\"exception\"), on=\"prompt\")\n", "\n", "try:\n", " guard(\n", @@ -159,7 +158,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.0" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/no_secrets_in_generated_text.ipynb b/docs/examples/no_secrets_in_generated_text.ipynb index 05f0ecc16..decaa7247 100644 --- a/docs/examples/no_secrets_in_generated_text.ipynb +++ b/docs/examples/no_secrets_in_generated_text.ipynb @@ -45,26 +45,7 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n", - "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/validators/__init__.py:51: FutureWarning: \n", - " Importing validators from `guardrails.validators` is deprecated.\n", - " All validators are now available in the Guardrails Hub. Please install\n", - " and import them from the hub instead. All validators will be\n", - " removed from this module in the next major release.\n", - "\n", - " Install with: `guardrails hub install hub:///`\n", - " Import as: from guardrails.hub import `ValidatorName`\n", - " \n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "from guardrails.validators import Validator, register_validator, PassResult, FailResult, ValidationResult\n", "\n", @@ -117,7 +98,7 @@ "\n", "How do I use OpenAI's Completion API?\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\n", @@ -145,7 +126,7 @@ "\n", "How do I use OpenAI's Completion API?\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "class ScrubbedCode(BaseModel):\n", @@ -187,16 +168,7 @@ "cell_type": "code", "execution_count": 5, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/prompt/base_prompt.py:59: FutureWarning: Prompt Primitives are moving! To keep the same behaviour, switch from `json` constants to `xml` constants. Example: ${gr.complete_json_suffix} -> ${gr.complete_xml_suffix}\n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] @@ -212,18 +184,42 @@ "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/prompt/base_prompt.py:59: FutureWarning: Prompt Primitives are moving! To keep the same behaviour, switch from `json` constants to `xml` constants. Example: ${gr.complete_json_suffix} -> ${gr.complete_xml_suffix}\n", - " warn(\n" - ] - } - ], + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=ScrubbedCode)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, "source": [ - "guard = gd.Guard.from_pydantic(output_class=ScrubbedCode, prompt=prompt)" + "## Step 3: Wrap the LLM API call with `Guard`" + ] + }, + { + "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 types." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.completions.create,\n", + " model=\"gpt-3.5-turbo-instruct\",\n", + " max_tokens=2048,\n", + " temperature=0,\n", + " prompt=prompt\n", + ")" ] }, { @@ -231,22 +227,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We see the prompt that will be sent to the LLM." + "We can see the prompt that was sent to the LLM." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 16, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/var/folders/w2/ssf16z690zd7_4dggw0y5s_m0000gn/T/ipykernel_82075/3983563700.py:1: DeprecationWarning: 'Guard.base_prompt' is deprecated and will be removed in versions 0.5.x and beyond. Use 'Guard.history.last.prompt' instead.\n", - " print(guard.base_prompt)\n" - ] - }, { "data": { "text/html": [ @@ -258,11 +246,10 @@ "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 description=\"Show an example curl command for using openai Completion API\" format=\"no-code-secrets\" \n", + "name=\"api_help\" required=\"true\"></string>\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", @@ -275,6 +262,10 @@ "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", "\n", "\n", + "\n", + "Json Output:\n", + "\n", + "\n", "\n" ], "text/plain": [ @@ -286,11 +277,10 @@ "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\u001b[35m/\u001b[0m\u001b[95mstring\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[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", @@ -302,6 +292,10 @@ "\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", + "\n", + "Json Output:\n", + "\n", "\n" ] }, @@ -310,51 +304,34 @@ } ], "source": [ - "print(guard.base_prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "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 types." + "print(guard.history.last.compiled_prompt)" ] }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.completions.create, model=\"gpt-3.5-turbo-instruct\", max_tokens=2048, temperature=0\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
{'api_help': 'Show an example curl command for using openai Completion API'}\n",
+       "
{\n",
+       "    'api_help': 'curl -X POST https://api.openai.com/v1/engines/{engine}/completions \\\\\\n-H \\'Content-Type: \n",
+       "application/json\\' \\\\\\n-H \\'Authorization: Bearer {YOUR_API_KEY}\\' \\\\\\n-d \\'{\"prompt\": \"The <YOUR PROMPT HERE>\", \n",
+       "\"max_tokens\": <INTEGER>, \"temperature\": <DECIMAL>, \"top_p\": <DECIMAL>, \"n\": <INTEGER>, \"stream\": <BOOLEAN>, \n",
+       "\"logprobs\": <INTEGER>, \"stop\": <STRING>, \"presence_penalty\": <DECIMAL>, \"frequency_penalty\": <DECIMAL>, \"best_of\": \n",
+       "<INTEGER>, \"logit_bias\": <OBJECT>}\\''\n",
+       "}\n",
        "
\n" ], "text/plain": [ - "\u001b[1m{\u001b[0m\u001b[32m'api_help'\u001b[0m: \u001b[32m'Show an example curl command for using openai Completion API'\u001b[0m\u001b[1m}\u001b[0m\n" + "\u001b[1m{\u001b[0m\n", + " \u001b[32m'api_help'\u001b[0m: \u001b[32m'curl -X POST https://api.openai.com/v1/engines/\u001b[0m\u001b[32m{\u001b[0m\u001b[32mengine\u001b[0m\u001b[32m}\u001b[0m\u001b[32m/completions \\\\\\n-H \\'Content-Type: \u001b[0m\n", + "\u001b[32mapplication/json\\' \\\\\\n-H \\'Authorization: Bearer \u001b[0m\u001b[32m{\u001b[0m\u001b[32mYOUR_API_KEY\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\' \\\\\\n-d \\'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"prompt\": \"The \u001b[0m\u001b[32m<\u001b[0m\u001b[32mYOUR\u001b[0m\u001b[32m PROMPT HERE>\", \u001b[0m\n", + "\u001b[32m\"max_tokens\": , \"temperature\": , \"top_p\": , \"n\": , \"stream\": , \u001b[0m\n", + "\u001b[32m\"logprobs\": , \"stop\": , \"presence_penalty\": , \"frequency_penalty\": , \"best_of\": \u001b[0m\n", + "\u001b[32m, \"logit_bias\": \u001b[0m\u001b[32m}\u001b[0m\u001b[32m\\''\u001b[0m\n", + "\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -367,42 +344,112 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Logs\n",
-       "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
+       "├── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\n",
+       "│   │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │ How do I use OpenAI's Completion API?                                                                   │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │ Given below is XML that describes the information to extract from this document and the tags to extract │ │\n",
+       "│   │ │ it into.                                                                                                │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │ <output>                                                                                                │ │\n",
+       "│   │ │   <string description=\"Show an example curl command for using openai Completion API\"                    │ │\n",
+       "│   │ │ format=\"no-code-secrets\" name=\"api_help\" required=\"true\"></string>                                      │ │\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 │ │\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",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │ Json Output:                                                                                            │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ │                                                                                                         │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   │ ╭──────────────────────────────────────────── Message History ────────────────────────────────────────────╮ │\n",
+       "│   │ │ No message history.                                                                                     │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
+       "│   │ │ {                                                                                                       │ │\n",
+       "│   │ │   \"api_help\": {                                                                                         │ │\n",
+       "│   │ │     \"description\": \"Show an example curl command for using openai Completion API\",                      │ │\n",
+       "│   │ │     \"format\": \"no-code-secrets\",                                                                        │ │\n",
+       "│   │ │     \"name\": \"api_help\",                                                                                 │ │\n",
+       "│   │ │     \"required\": true                                                                                    │ │\n",
+       "│   │ │   }                                                                                                     │ │\n",
+       "│   │ │ }                                                                                                       │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
+       "│   │ │ SkeletonReAsk(                                                                                          │ │\n",
+       "│   │ │     incorrect_value={'api_help': {}},                                                                   │ │\n",
+       "│   │ │     fail_results=[                                                                                      │ │\n",
+       "│   │ │         FailResult(                                                                                     │ │\n",
+       "│   │ │             outcome='fail',                                                                             │ │\n",
+       "│   │ │             error_message='JSON does not match schema:\\n{\\n  \"$.api_help\": [\\n    \"{} is not of type    │ │\n",
+       "│   │ │ \\'string\\'\"\\n  ]\\n}',                                                                                   │ │\n",
+       "│   │ │             fix_value=None,                                                                             │ │\n",
+       "│   │ │             metadata=None,                                                                              │ │\n",
+       "│   │ │             validated_chunk=None,                                                                       │ │\n",
+       "│   │ │             error_spans=None                                                                            │ │\n",
+       "│   │ │         )                                                                                               │ │\n",
+       "│   │ │     ]                                                                                                   │ │\n",
+       "│   │ │ )                                                                                                       │ │\n",
+       "│   │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
+       "│   ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
+       "└── ╭────────────────────────────────────────────────── Step 1 ───────────────────────────────────────────────────╮\n",
        "    │ ╭──────────────────────────────────────────────── Prompt ─────────────────────────────────────────────────╮ │\n",
        "    │ │                                                                                                         │ │\n",
+       "    │ │ I was given the following JSON response, which had problems due to incorrect values.                    │ │\n",
+       "    │ │                                                                                                         │ │\n",
+       "    │ │ {                                                                                                       │ │\n",
+       "    │ │   \"incorrect_value\": {                                                                                  │ │\n",
+       "    │ │     \"api_help\": {}                                                                                      │ │\n",
+       "    │ │   },                                                                                                    │ │\n",
+       "    │ │   \"error_messages\": [                                                                                   │ │\n",
+       "    │ │     \"JSON does not match schema:\\n{\\n  \\\"$.api_help\\\": [\\n    \\\"{} is not of type 'string'\\\"\\n  ]\\n}\"   │ │\n",
+       "    │ │   ]                                                                                                     │ │\n",
+       "    │ │ }                                                                                                       │ │\n",
        "    │ │                                                                                                         │ │\n",
-       "    │ │ How do I use OpenAI's Completion API?                                                                   │ │\n",
+       "    │ │ Help me correct the incorrect values based on the given error messages.                                 │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ Given below is XML that describes the information to extract from this document and the tags to extract │ │\n",
        "    │ │ 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 description=\"Show an example curl command for using openai Completion API\"                    │ │\n",
+       "    │ │ format=\"no-code-secrets\" name=\"api_help\" required=\"true\"></string>                                      │ │\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 │ │\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",
+       "    │ │ Here's an example of the structure:                                                                     │ │\n",
+       "    │ │ {                                                                                                       │ │\n",
+       "    │ │   \"api_help\": \"cup\"                                                                                     │ │\n",
+       "    │ │ }                                                                                                       │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │                                                                                                         │ │\n",
        "    │ │ Json Output:                                                                                            │ │\n",
@@ -414,46 +461,127 @@
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n",
        "    │ │ {                                                                                                       │ │\n",
-       "    │ │     \"api_help\": \"Show an example curl command for using openai Completion API\"                          │ │\n",
+       "    │ │   \"api_help\": \"curl -X POST https://api.openai.com/v1/engines/{engine}/completions \\\\\\n-H               │ │\n",
+       "    │ │ 'Content-Type: application/json' \\\\\\n-H 'Authorization: Bearer {YOUR_API_KEY}' \\\\\\n-d '{\\\"prompt\\\":     │ │\n",
+       "    │ │ \\\"The <YOUR PROMPT HERE>\\\", \\\"max_tokens\\\": <INTEGER>, \\\"temperature\\\": <DECIMAL>, \\\"top_p\\\":           │ │\n",
+       "    │ │ <DECIMAL>, \\\"n\\\": <INTEGER>, \\\"stream\\\": <BOOLEAN>, \\\"logprobs\\\": <INTEGER>, \\\"stop\\\": <STRING>,        │ │\n",
+       "    │ │ \\\"presence_penalty\\\": <DECIMAL>, \\\"frequency_penalty\\\": <DECIMAL>, \\\"best_of\\\": <INTEGER>,              │ │\n",
+       "    │ │ \\\"logit_bias\\\": <OBJECT>}'\"                                                                             │ │\n",
        "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n",
-       "    │ │ {'api_help': 'Show an example curl command for using openai Completion API'}                            │ │\n",
+       "    │ │ {                                                                                                       │ │\n",
+       "    │ │     'api_help': 'curl -X POST https://api.openai.com/v1/engines/{engine}/completions \\\\\\n-H             │ │\n",
+       "    │ │ \\'Content-Type: application/json\\' \\\\\\n-H \\'Authorization: Bearer {YOUR_API_KEY}\\' \\\\\\n-d \\'{\"prompt\":  │ │\n",
+       "    │ │ \"The <YOUR PROMPT HERE>\", \"max_tokens\": <INTEGER>, \"temperature\": <DECIMAL>, \"top_p\": <DECIMAL>, \"n\":   │ │\n",
+       "    │ │ <INTEGER>, \"stream\": <BOOLEAN>, \"logprobs\": <INTEGER>, \"stop\": <STRING>, \"presence_penalty\": <DECIMAL>, │ │\n",
+       "    │ │ \"frequency_penalty\": <DECIMAL>, \"best_of\": <INTEGER>, \"logit_bias\": <OBJECT>}\\''                        │ │\n",
+       "    │ │ }                                                                                                       │ │\n",
        "    │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n",
        "    ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n",
        "
\n" ], "text/plain": [ "Logs\n", - "└── ╭────────────────────────────────────────────────── Step 0 ───────────────────────────────────────────────────╮\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", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHow do I use OpenAI's Completion API?\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 below is XML that describes the information to extract from this document and the tags to extract\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \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 │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \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;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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;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 \"api_help\": {\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Show an example curl command for using openai Completion API\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"format\": \"no-code-secrets\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"api_help\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"required\": 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 }\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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;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={'api_help': {}},\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 error_message='JSON does not match schema:\\n{\\n \"$.api_help\": [\\n \"{} is not of type \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + "│ │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m\\'string\\'\"\\n ]\\n}',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 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 validated_chunk=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_spans=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", " │ \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", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m{\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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 \"api_help\": {}\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m },\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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:\\n{\\n \\\"$.api_help\\\": [\\n \\\"{} is not of type 'string'\\\"\\n ]\\n}\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m ]\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m}\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mHow do I use OpenAI's Completion API?\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 below is XML that describes the information to extract from this document and the tags to extract\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \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;255mHere's an example of the structure:\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m{\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \"api_help\": \"cup\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m}\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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", @@ -465,11 +593,22 @@ " │ \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 \"api_help\": \"Show an example curl command for using openai Completion API\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 https://api.openai.com/v1/engines/{engine}/completions \\\\\\n-H \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m'Content-Type: application/json' \\\\\\n-H 'Authorization: Bearer {YOUR_API_KEY}' \\\\\\n-d '{\\\"prompt\\\": \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m\\\"The \\\", \\\"max_tokens\\\": , \\\"temperature\\\": , \\\"top_p\\\": \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m, \\\"n\\\": , \\\"stream\\\": , \\\"logprobs\\\": , \\\"stop\\\": , \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m\\\"presence_penalty\\\": , \\\"frequency_penalty\\\": , \\\"best_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;220m\\\"logit_bias\\\": }'\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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{'api_help': 'Show an example curl command for using openai Completion API'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 'api_help': 'curl -X POST https://api.openai.com/v1/engines/{engine}/completions \\\\\\n-H \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m\\'Content-Type: application/json\\' \\\\\\n-H \\'Authorization: Bearer {YOUR_API_KEY}\\' \\\\\\n-d \\'{\"prompt\": \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m\"The \", \"max_tokens\": , \"temperature\": , \"top_p\": , \"n\": \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m, \"stream\": , \"logprobs\": , \"stop\": , \"presence_penalty\": ,\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m\"frequency_penalty\": , \"best_of\": , \"logit_bias\": }\\''\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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" ] @@ -499,7 +638,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/provenance.ipynb b/docs/examples/provenance.ipynb index 1b9e5e45a..3297601c0 100644 --- a/docs/examples/provenance.ipynb +++ b/docs/examples/provenance.ipynb @@ -2,12 +2,31 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mprovenance_embeddings...\u001b[0m\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", + " warnings.warn(\n", + "✅Successfully installed guardrails/provenance_embeddings!\n", + "\n", + "\n", + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mprovenance_llm...\u001b[0m\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", + " warnings.warn(\n", + "✅Successfully installed guardrails/provenance_llm!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/provenance_embeddings\n", - "!guardrails hub install hub://guardrails/provenance_llm" + "!guardrails hub install hub://guardrails/provenance_embeddings --quiet\n", + "!guardrails hub install hub://guardrails/provenance_llm --quiet" ] }, { @@ -57,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -269,9 +288,18 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ "# Create an embedding function that uses a cohere model to embed the text\n", "from rich import print\n", @@ -310,7 +338,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -332,7 +360,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -389,7 +417,7 @@ " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] }, - "execution_count": 25, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -414,7 +442,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -457,7 +485,7 @@ " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] }, - "execution_count": 16, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -496,7 +524,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -518,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -540,11 +568,11 @@ " │ │ important to clean the litter box regularly and to provide a safe and healthy environment for the cat. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ 'To retrain a cat to use the litter box, put its litter box in a low traffic area with at least 2 exits │ │\n", + " │ │ To retrain a cat to use the litter box, put its litter box in a low traffic area with at least 2 exits │ │\n", " │ │ so the cat doesn’t feel cornered. Find the right litterbox for your cat, as well as the right litter. │ │\n", " │ │ Place the litterbox in a good spot, away from heavily trafficked and noisy areas. Keep the litterbox │ │\n", " │ │ very clean, and do not punish the cat or confine her to just one room. Once training is complete, it is │ │\n", - " │ │ important to clean the litter box regularly and to provide a safe and healthy environment for the cat.' │ │\n", + " │ │ important to clean the litter box regularly and to provide a safe and healthy environment for the cat. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "\n" @@ -566,16 +594,16 @@ " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mimportant to clean the litter box regularly and to provide a safe and healthy environment for the cat.\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[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'To retrain a cat to use the litter box, put its litter box in a low traffic area with at least 2 exits\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\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 retrain a cat to use the litter box, put its litter box in a low traffic area with at least 2 exits \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mso the cat doesn’t feel cornered. Find the right litterbox for your cat, as well as the right litter. \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mPlace the litterbox in a good spot, away from heavily trafficked and noisy areas. Keep the litterbox \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mvery clean, and do not punish the cat or confine her to just one room. Once training is complete, it is\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mimportant to clean the litter box regularly and to provide a safe and healthy environment for the cat.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mimportant to clean the litter box regularly and to provide a safe and healthy environment for the cat.\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[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": 18, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -594,7 +622,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -637,7 +665,7 @@ " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n" ] }, - "execution_count": 19, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -677,7 +705,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/examples/recipe_generation.ipynb b/docs/examples/recipe_generation.ipynb index 7996df62e..7c62bf769 100644 --- a/docs/examples/recipe_generation.ipynb +++ b/docs/examples/recipe_generation.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -46,7 +46,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -119,7 +119,7 @@ "\n", "\n", "Generate a recipe for vegan mac and cheese.\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\n", @@ -135,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -145,7 +145,7 @@ "\n", "prompt = \"\"\"\n", "Generate a recipe for vegan mac and cheese.\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "class Ingredient(BaseModel):\n", @@ -197,57 +197,81 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_rail_string(rail_str)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the Pydantic model:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages/guardrails/validatorsattr.py:285: UserWarning: Validator 1-indexed is not valid for element integer.\n", - " warnings.warn(\n", - "\n", - "/home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages/guardrails/validatorsattr.py:285: UserWarning: Validator units-imperial is not valid for element float.\n", - " warnings.warn(\n", - "\n", - "/home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages/guardrails/validatorsattr.py:285: UserWarning: Validator units-imperial is not valid for element string.\n", - " warnings.warn(\n", - "\n" + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/guardrails/validator_base.py:144: UserWarning: Validator with id 1-indexed was not found in the registry! Ignoring...\n", + " warn(f\"Validator with id {name} was not found in the registry! Ignoring...\")\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/guardrails/validator_base.py:144: UserWarning: Validator with id units-imperial was not found in the registry! Ignoring...\n", + " warn(f\"Validator with id {name} was not found in the registry! Ignoring...\")\n" ] } ], "source": [ - "guard = gd.Guard.from_rail_string(rail_str)" + "guard = gd.Guard.from_pydantic(output_class=Recipe)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "From the Pydantic model:" + "As we can see, a few formatters weren't supported. These formatters won't be enforced in the output, but this information can still be used to generate a prompt." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wrap the LLM API call with `Guard`" ] }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=Recipe, prompt=prompt)" + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " max_tokens=2048,\n", + " temperature=0,\n", + " model=\"gpt-4\"\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "As we can see, a few formatters weren't supported. These formatters won't be enforced in the output, but this information can still be used to generate a prompt.\n", - "\n", - "We see the prompt that will be sent to the LLM. The `{document}` is substituted with the user provided value at runtime." + "We can see the prompt that was sent to the LLM. The `{document}` param was substituted with the user provided value at runtime." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -259,25 +283,24 @@ "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=\"ingredients\" description=\"What are the ingredients for the recipe?\">\n", - " <object>\n", - " <integer name=\"index\" format=\"1-indexed\"/>\n", - " <string name=\"name\" format=\"is-vegan\"/>\n", - " <string name=\"brand\" description=\"Suggested brand for the ingredient (if any)\"/>\n", - " <bool name=\"optional\" description=\"Is the ingredient necessary?\"/>\n", - " <float name=\"quantity\" description=\"how much of this ingredient to use\" format=\"units-imperial\"/>\n", - " <string name=\"units\" format=\"units-imperial\"/>\n", - " </object>\n", - " </list>\n", - " <list name=\"instructions\" description=\"What are the instructions for the recipe?\">\n", - " <object>\n", - " <integer name=\"index\" format=\"1-indexed\"/>\n", - " <string name=\"step\"/>\n", - " </object>\n", - " </list>\n", + " <list description=\"What are the ingredients for the recipe?\" name=\"ingredients\" required=\"true\">\n", + " <object required=\"true\">\n", + " <integer name=\"index\" required=\"true\"></integer>\n", + " <string format=\"is-vegan\" name=\"name\" required=\"true\"></string>\n", + " <string description=\"Suggested brand for the ingredient (if any)\" name=\"brand\" required=\"true\"></string>\n", + " <bool description=\"Is the ingredient necessary?\" name=\"optional\" required=\"true\"></bool>\n", + " <float description=\"how much of this ingredient to use\" name=\"quantity\" required=\"true\"></float>\n", + " <string name=\"units\" required=\"true\"></string>\n", + " </object>\n", + " </list>\n", + " <list description=\"What are the instructions for the recipe?\" name=\"instructions\" required=\"true\">\n", + " <object required=\"true\">\n", + " <integer name=\"index\" required=\"true\"></integer>\n", + " <string name=\"step\" required=\"true\"></string>\n", + " </object>\n", + " </list>\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", @@ -299,25 +322,24 @@ "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\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", - "\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", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95minteger\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mbool\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mfloat\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\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", + "\u001b[39m \u001b[0m\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95minteger\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\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", "\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", @@ -337,39 +359,7 @@ } ], "source": [ - "print(guard.base_prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " max_tokens=2048,\n", - " temperature=0,\n", - " model=\"gpt-4\"\n", - ")" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -383,7 +373,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -391,74 +381,75 @@ "text/html": [ "
{\n",
        "    'ingredients': [\n",
-       "        {'index': 1, 'name': 'macaroni', 'brand': 'None', 'optional': False, 'quantity': 8.0, 'units': 'ounces'},\n",
-       "        {'index': 2, 'name': 'almond milk', 'brand': 'Silk', 'optional': False, 'quantity': 1.0, 'units': 'cup'},\n",
        "        {\n",
-       "            'index': 3,\n",
-       "            'name': 'nutritional yeast',\n",
-       "            'brand': 'Bragg',\n",
+       "            'index': 1,\n",
+       "            'name': 'macaroni',\n",
+       "            'brand': 'Barilla',\n",
        "            'optional': False,\n",
-       "            'quantity': 0.25,\n",
-       "            'units': 'cup'\n",
+       "            'quantity': 8.0,\n",
+       "            'units': 'ounces'\n",
        "        },\n",
        "        {\n",
-       "            'index': 4,\n",
-       "            'name': 'tahini',\n",
-       "            'brand': 'None',\n",
+       "            'index': 2,\n",
+       "            'name': 'cashews',\n",
+       "            'brand': 'Whole Foods',\n",
        "            'optional': False,\n",
-       "            'quantity': 2.0,\n",
-       "            'units': 'tablespoons'\n",
+       "            'quantity': 1.0,\n",
+       "            'units': 'cup'\n",
        "        },\n",
        "        {\n",
-       "            'index': 5,\n",
-       "            'name': 'lemon juice',\n",
-       "            'brand': 'None',\n",
+       "            'index': 3,\n",
+       "            'name': 'nutritional yeast',\n",
+       "            'brand': \"Bob's Red Mill\",\n",
        "            'optional': False,\n",
-       "            'quantity': 1.0,\n",
-       "            'units': 'tablespoon'\n",
+       "            'quantity': 0.25,\n",
+       "            'units': 'cup'\n",
        "        },\n",
        "        {\n",
-       "            'index': 6,\n",
+       "            'index': 4,\n",
        "            'name': 'garlic powder',\n",
-       "            'brand': 'None',\n",
+       "            'brand': 'McCormick',\n",
        "            'optional': False,\n",
        "            'quantity': 0.5,\n",
        "            'units': 'teaspoon'\n",
        "        },\n",
        "        {\n",
-       "            'index': 7,\n",
+       "            'index': 5,\n",
        "            'name': 'onion powder',\n",
-       "            'brand': 'None',\n",
+       "            'brand': 'McCormick',\n",
        "            'optional': False,\n",
        "            'quantity': 0.5,\n",
        "            'units': 'teaspoon'\n",
        "        },\n",
        "        {\n",
-       "            'index': 8,\n",
+       "            'index': 6,\n",
        "            'name': 'turmeric',\n",
-       "            'brand': 'None',\n",
+       "            'brand': 'Simply Organic',\n",
        "            'optional': True,\n",
        "            'quantity': 0.25,\n",
        "            'units': 'teaspoon'\n",
        "        },\n",
-       "        {'index': 9, 'name': 'salt', 'brand': 'None', 'optional': True, 'quantity': 0.5, 'units': 'teaspoon'}\n",
-       "    ],\n",
-       "    'instructions': [\n",
+       "        {'index': 7, 'name': 'salt', 'brand': 'Morton', 'optional': False, 'quantity': 0.5, 'units': 'teaspoon'},\n",
        "        {\n",
-       "            'index': 1,\n",
-       "            'step': 'Cook the macaroni according to the package instructions, then drain and set aside.'\n",
+       "            'index': 8,\n",
+       "            'name': 'pepper',\n",
+       "            'brand': 'McCormick',\n",
+       "            'optional': True,\n",
+       "            'quantity': 0.25,\n",
+       "            'units': 'teaspoon'\n",
        "        },\n",
+       "        {'index': 9, 'name': 'water', 'brand': '', 'optional': False, 'quantity': 1.5, 'units': 'cups'}\n",
+       "    ],\n",
+       "    'instructions': [\n",
+       "        {'index': 1, 'step': 'Soak the cashews in water for at least 2 hours, then drain.'},\n",
+       "        {'index': 2, 'step': 'Cook the macaroni according to the package instructions, then drain.'},\n",
        "        {\n",
-       "            'index': 2,\n",
-       "            'step': 'In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, garlic powder, \n",
-       "onion powder, turmeric (if using), and salt (if using). Blend until smooth.'\n",
+       "            'index': 3,\n",
+       "            'step': 'In a blender, combine the soaked cashews, nutritional yeast, garlic powder, onion powder, \n",
+       "turmeric (if using), salt, pepper (if using), and water. Blend until smooth.'\n",
        "        },\n",
-       "        {'index': 3, 'step': 'Pour the sauce over the cooked macaroni and stir to combine.'},\n",
-       "        {\n",
-       "            'index': 4,\n",
-       "            'step': 'Serve the vegan mac and cheese hot, with additional nutritional yeast or other toppings if \n",
-       "desired.'\n",
-       "        }\n",
+       "        {'index': 4, 'step': 'Pour the sauce over the cooked macaroni and stir to combine.'},\n",
+       "        {'index': 5, 'step': 'Serve the vegan mac and cheese hot.'}\n",
        "    ]\n",
        "}\n",
        "
\n" @@ -466,74 +457,75 @@ "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'None'\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'ounces'\u001b[0m\u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m, \u001b[32m'name'\u001b[0m: \u001b[32m'almond milk'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m'Silk'\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'cup'\u001b[0m\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'nutritional yeast'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'Bragg'\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'macaroni'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[32m'Barilla'\u001b[0m,\n", " \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m,\n", - " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.25\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'cup'\u001b[0m\n", + " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m8.0\u001b[0m,\n", + " \u001b[32m'units'\u001b[0m: \u001b[32m'ounces'\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'tahini'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'None'\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m2\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'cashews'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[32m'Whole Foods'\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'tablespoons'\u001b[0m\n", + " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.0\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;36m5\u001b[0m,\n", - " \u001b[32m'name'\u001b[0m: \u001b[32m'lemon juice'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'None'\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\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;36m1.0\u001b[0m,\n", - " \u001b[32m'units'\u001b[0m: \u001b[32m'tablespoon'\u001b[0m\n", + " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.25\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;36m6\u001b[0m,\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'garlic powder'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'None'\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'teaspoon'\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'index'\u001b[0m: \u001b[1;36m5\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'onion powder'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'None'\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'teaspoon'\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'index'\u001b[0m: \u001b[1;36m6\u001b[0m,\n", " \u001b[32m'name'\u001b[0m: \u001b[32m'turmeric'\u001b[0m,\n", - " \u001b[32m'brand'\u001b[0m: \u001b[32m'None'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[32m'Simply Organic'\u001b[0m,\n", " \u001b[32m'optional'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.25\u001b[0m,\n", " \u001b[32m'units'\u001b[0m: \u001b[32m'teaspoon'\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'None'\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;92mTrue\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.5\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'teaspoon'\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\u001b[32m'index'\u001b[0m: \u001b[1;36m7\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;36m0.5\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'teaspoon'\u001b[0m\u001b[1m}\u001b[0m,\n", " \u001b[1m{\u001b[0m\n", - " \u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m,\n", - " \u001b[32m'step'\u001b[0m: \u001b[32m'Cook the macaroni according to the package instructions, then drain and set aside.'\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m8\u001b[0m,\n", + " \u001b[32m'name'\u001b[0m: \u001b[32m'pepper'\u001b[0m,\n", + " \u001b[32m'brand'\u001b[0m: \u001b[32m'McCormick'\u001b[0m,\n", + " \u001b[32m'optional'\u001b[0m: \u001b[3;92mTrue\u001b[0m,\n", + " \u001b[32m'quantity'\u001b[0m: \u001b[1;36m0.25\u001b[0m,\n", + " \u001b[32m'units'\u001b[0m: \u001b[32m'teaspoon'\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'water'\u001b[0m, \u001b[32m'brand'\u001b[0m: \u001b[32m''\u001b[0m, \u001b[32m'optional'\u001b[0m: \u001b[3;91mFalse\u001b[0m, \u001b[32m'quantity'\u001b[0m: \u001b[1;36m1.5\u001b[0m, \u001b[32m'units'\u001b[0m: \u001b[32m'cups'\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\u001b[32m'index'\u001b[0m: \u001b[1;36m1\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Soak the cashews in water for at least 2 hours, then drain.'\u001b[0m\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'Cook the macaroni according to the package instructions, then drain.'\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'In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, garlic powder, \u001b[0m\n", - "\u001b[32monion powder, turmeric \u001b[0m\u001b[32m(\u001b[0m\u001b[32mif using\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, and salt \u001b[0m\u001b[32m(\u001b[0m\u001b[32mif using\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. Blend until smooth.'\u001b[0m\n", + " \u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m,\n", + " \u001b[32m'step'\u001b[0m: \u001b[32m'In a blender, combine the soaked cashews, nutritional yeast, garlic powder, onion powder, \u001b[0m\n", + "\u001b[32mturmeric \u001b[0m\u001b[32m(\u001b[0m\u001b[32mif using\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, salt, pepper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mif using\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, and water. Blend until smooth.'\u001b[0m\n", " \u001b[1m}\u001b[0m,\n", - " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m3\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Pour the sauce over the cooked macaroni and stir to combine.'\u001b[0m\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'Serve the vegan mac and cheese hot, with additional nutritional yeast or other toppings if \u001b[0m\n", - "\u001b[32mdesired.'\u001b[0m\n", - " \u001b[1m}\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m4\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Pour the sauce over the cooked macaroni and stir to combine.'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'index'\u001b[0m: \u001b[1;36m5\u001b[0m, \u001b[32m'step'\u001b[0m: \u001b[32m'Serve the vegan mac and cheese hot.'\u001b[0m\u001b[1m}\u001b[0m\n", " \u001b[1m]\u001b[0m\n", "\u001b[1m}\u001b[0m\n" ] @@ -548,7 +540,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -564,26 +556,25 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <list name=\"ingredients\" description=\"What are the ingredients for the recipe?\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <integer name=\"index\" format=\"1-indexed\"/> │ │\n", - " │ │ <string name=\"name\" format=\"is-vegan\"/> │ │\n", - " │ │ <string name=\"brand\" description=\"Suggested brand for the ingredient (if any)\"/> │ │\n", - " │ │ <bool name=\"optional\" description=\"Is the ingredient necessary?\"/> │ │\n", - " │ │ <float name=\"quantity\" description=\"how much of this ingredient to use\" │ │\n", - " │ │ format=\"units-imperial\"/> │ │\n", - " │ │ <string name=\"units\" format=\"units-imperial\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", - " │ │ <list name=\"instructions\" description=\"What are the instructions for the recipe?\"> │ │\n", - " │ │ <object> │ │\n", - " │ │ <integer name=\"index\" format=\"1-indexed\"/> │ │\n", - " │ │ <string name=\"step\"/> │ │\n", - " │ │ </object> │ │\n", - " │ │ </list> │ │\n", + " │ │ <list description=\"What are the ingredients for the recipe?\" name=\"ingredients\" required=\"true\"> │ │\n", + " │ │ <object required=\"true\"> │ │\n", + " │ │ <integer name=\"index\" required=\"true\"></integer> │ │\n", + " │ │ <string format=\"is-vegan\" name=\"name\" required=\"true\"></string> │ │\n", + " │ │ <string description=\"Suggested brand for the ingredient (if any)\" name=\"brand\" │ │\n", + " │ │ required=\"true\"></string> │ │\n", + " │ │ <bool description=\"Is the ingredient necessary?\" name=\"optional\" required=\"true\"></bool> │ │\n", + " │ │ <float description=\"how much of this ingredient to use\" name=\"quantity\" required=\"true\"></float> │ │\n", + " │ │ <string name=\"units\" required=\"true\"></string> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\n", + " │ │ <list description=\"What are the instructions for the recipe?\" name=\"instructions\" required=\"true\"> │ │\n", + " │ │ <object required=\"true\"> │ │\n", + " │ │ <integer name=\"index\" required=\"true\"></integer> │ │\n", + " │ │ <string name=\"step\" required=\"true\"></string> │ │\n", + " │ │ </object> │ │\n", + " │ │ </list> │ │\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 │ │\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", @@ -607,102 +598,105 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", + " │ │ │ │\n", " │ │ { │ │\n", - " │ │ \"ingredients\": [ │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 1, │ │\n", - " │ │ \"name\": \"macaroni\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 8.0, │ │\n", - " │ │ \"units\": \"ounces\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 2, │ │\n", - " │ │ \"name\": \"almond milk\", │ │\n", - " │ │ \"brand\": \"Silk\", │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 1.0, │ │\n", - " │ │ \"units\": \"cup\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 3, │ │\n", - " │ │ \"name\": \"nutritional yeast\", │ │\n", - " │ │ \"brand\": \"Bragg\", │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 0.25, │ │\n", - " │ │ \"units\": \"cup\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 4, │ │\n", - " │ │ \"name\": \"tahini\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 2.0, │ │\n", - " │ │ \"units\": \"tablespoons\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 5, │ │\n", - " │ │ \"name\": \"lemon juice\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 1.0, │ │\n", - " │ │ \"units\": \"tablespoon\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 6, │ │\n", - " │ │ \"name\": \"garlic powder\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 0.5, │ │\n", - " │ │ \"units\": \"teaspoon\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 7, │ │\n", - " │ │ \"name\": \"onion powder\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": false, │ │\n", - " │ │ \"quantity\": 0.5, │ │\n", - " │ │ \"units\": \"teaspoon\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 8, │ │\n", - " │ │ \"name\": \"turmeric\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": true, │ │\n", - " │ │ \"quantity\": 0.25, │ │\n", - " │ │ \"units\": \"teaspoon\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 9, │ │\n", - " │ │ \"name\": \"salt\", │ │\n", - " │ │ \"brand\": null, │ │\n", - " │ │ \"optional\": true, │ │\n", - " │ │ \"quantity\": 0.5, │ │\n", - " │ │ \"units\": \"teaspoon\" │ │\n", - " │ │ } │ │\n", - " │ │ ], │ │\n", - " │ │ \"instructions\": [ │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 1, │ │\n", - " │ │ \"step\": \"Cook the macaroni according to the package instructions, then drain and set │ │\n", - " │ │ aside.\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 2, │ │\n", - " │ │ \"step\": \"In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, │ │\n", - " │ │ garlic powder, onion powder, turmeric (if using), and salt (if using). Blend until smooth.\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 3, │ │\n", - " │ │ \"step\": \"Pour the sauce over the cooked macaroni and stir to combine.\" │ │\n", - " │ │ }, │ │\n", - " │ │ { │ │\n", - " │ │ \"index\": 4, │ │\n", - " │ │ \"step\": \"Serve the vegan mac and cheese hot, with additional nutritional yeast or other │ │\n", - " │ │ toppings if desired.\" │ │\n", - " │ │ } │ │\n", - " │ │ ] │ │\n", + " │ │ \"ingredients\": [ │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 1, │ │\n", + " │ │ \"name\": \"macaroni\", │ │\n", + " │ │ \"brand\": \"Barilla\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 8, │ │\n", + " │ │ \"units\": \"ounces\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 2, │ │\n", + " │ │ \"name\": \"cashews\", │ │\n", + " │ │ \"brand\": \"Whole Foods\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 1, │ │\n", + " │ │ \"units\": \"cup\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 3, │ │\n", + " │ │ \"name\": \"nutritional yeast\", │ │\n", + " │ │ \"brand\": \"Bob's Red Mill\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 0.25, │ │\n", + " │ │ \"units\": \"cup\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 4, │ │\n", + " │ │ \"name\": \"garlic powder\", │ │\n", + " │ │ \"brand\": \"McCormick\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 0.5, │ │\n", + " │ │ \"units\": \"teaspoon\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 5, │ │\n", + " │ │ \"name\": \"onion powder\", │ │\n", + " │ │ \"brand\": \"McCormick\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 0.5, │ │\n", + " │ │ \"units\": \"teaspoon\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 6, │ │\n", + " │ │ \"name\": \"turmeric\", │ │\n", + " │ │ \"brand\": \"Simply Organic\", │ │\n", + " │ │ \"optional\": true, │ │\n", + " │ │ \"quantity\": 0.25, │ │\n", + " │ │ \"units\": \"teaspoon\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 7, │ │\n", + " │ │ \"name\": \"salt\", │ │\n", + " │ │ \"brand\": \"Morton\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 0.5, │ │\n", + " │ │ \"units\": \"teaspoon\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 8, │ │\n", + " │ │ \"name\": \"pepper\", │ │\n", + " │ │ \"brand\": \"McCormick\", │ │\n", + " │ │ \"optional\": true, │ │\n", + " │ │ \"quantity\": 0.25, │ │\n", + " │ │ \"units\": \"teaspoon\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 9, │ │\n", + " │ │ \"name\": \"water\", │ │\n", + " │ │ \"brand\": \"\", │ │\n", + " │ │ \"optional\": false, │ │\n", + " │ │ \"quantity\": 1.5, │ │\n", + " │ │ \"units\": \"cups\" │ │\n", + " │ │ } │ │\n", + " │ │ ], │ │\n", + " │ │ \"instructions\": [ │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 1, │ │\n", + " │ │ \"step\": \"Soak the cashews in water for at least 2 hours, then drain.\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 2, │ │\n", + " │ │ \"step\": \"Cook the macaroni according to the package instructions, then drain.\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 3, │ │\n", + " │ │ \"step\": \"In a blender, combine the soaked cashews, nutritional yeast, garlic powder, onion │ │\n", + " │ │ powder, turmeric (if using), salt, pepper (if using), and water. Blend until smooth.\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 4, │ │\n", + " │ │ \"step\": \"Pour the sauce over the cooked macaroni and stir to combine.\" │ │\n", + " │ │ }, │ │\n", + " │ │ { │ │\n", + " │ │ \"index\": 5, │ │\n", + " │ │ \"step\": \"Serve the vegan mac and cheese hot.\" │ │\n", + " │ │ } │ │\n", + " │ │ ] │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", @@ -711,15 +705,15 @@ " │ │ { │ │\n", " │ │ 'index': 1, │ │\n", " │ │ 'name': 'macaroni', │ │\n", - " │ │ 'brand': 'None', │ │\n", + " │ │ 'brand': 'Barilla', │ │\n", " │ │ 'optional': False, │ │\n", " │ │ 'quantity': 8.0, │ │\n", " │ │ 'units': 'ounces' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 2, │ │\n", - " │ │ 'name': 'almond milk', │ │\n", - " │ │ 'brand': 'Silk', │ │\n", + " │ │ 'name': 'cashews', │ │\n", + " │ │ 'brand': 'Whole Foods', │ │\n", " │ │ 'optional': False, │ │\n", " │ │ 'quantity': 1.0, │ │\n", " │ │ 'units': 'cup' │ │\n", @@ -727,80 +721,79 @@ " │ │ { │ │\n", " │ │ 'index': 3, │ │\n", " │ │ 'name': 'nutritional yeast', │ │\n", - " │ │ 'brand': 'Bragg', │ │\n", + " │ │ 'brand': \"Bob's Red Mill\", │ │\n", " │ │ 'optional': False, │ │\n", " │ │ 'quantity': 0.25, │ │\n", " │ │ 'units': 'cup' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 4, │ │\n", - " │ │ 'name': 'tahini', │ │\n", - " │ │ 'brand': 'None', │ │\n", + " │ │ 'name': 'garlic powder', │ │\n", + " │ │ 'brand': 'McCormick', │ │\n", " │ │ 'optional': False, │ │\n", - " │ │ 'quantity': 2.0, │ │\n", - " │ │ 'units': 'tablespoons' │ │\n", + " │ │ 'quantity': 0.5, │ │\n", + " │ │ 'units': 'teaspoon' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 5, │ │\n", - " │ │ 'name': 'lemon juice', │ │\n", - " │ │ 'brand': 'None', │ │\n", + " │ │ 'name': 'onion powder', │ │\n", + " │ │ 'brand': 'McCormick', │ │\n", " │ │ 'optional': False, │ │\n", - " │ │ 'quantity': 1.0, │ │\n", - " │ │ 'units': 'tablespoon' │ │\n", + " │ │ 'quantity': 0.5, │ │\n", + " │ │ 'units': 'teaspoon' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 6, │ │\n", - " │ │ 'name': 'garlic powder', │ │\n", - " │ │ 'brand': 'None', │ │\n", - " │ │ 'optional': False, │ │\n", - " │ │ 'quantity': 0.5, │ │\n", + " │ │ 'name': 'turmeric', │ │\n", + " │ │ 'brand': 'Simply Organic', │ │\n", + " │ │ 'optional': True, │ │\n", + " │ │ 'quantity': 0.25, │ │\n", " │ │ 'units': 'teaspoon' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 7, │ │\n", - " │ │ 'name': 'onion powder', │ │\n", - " │ │ 'brand': 'None', │ │\n", + " │ │ 'name': 'salt', │ │\n", + " │ │ 'brand': 'Morton', │ │\n", " │ │ 'optional': False, │ │\n", " │ │ 'quantity': 0.5, │ │\n", " │ │ 'units': 'teaspoon' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 8, │ │\n", - " │ │ 'name': 'turmeric', │ │\n", - " │ │ 'brand': 'None', │ │\n", + " │ │ 'name': 'pepper', │ │\n", + " │ │ 'brand': 'McCormick', │ │\n", " │ │ 'optional': True, │ │\n", " │ │ 'quantity': 0.25, │ │\n", " │ │ 'units': 'teaspoon' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 9, │ │\n", - " │ │ 'name': 'salt', │ │\n", - " │ │ 'brand': 'None', │ │\n", - " │ │ 'optional': True, │ │\n", - " │ │ 'quantity': 0.5, │ │\n", - " │ │ 'units': 'teaspoon' │ │\n", + " │ │ 'name': 'water', │ │\n", + " │ │ 'brand': '', │ │\n", + " │ │ 'optional': False, │ │\n", + " │ │ 'quantity': 1.5, │ │\n", + " │ │ 'units': 'cups' │ │\n", " │ │ } │ │\n", " │ │ ], │ │\n", " │ │ 'instructions': [ │ │\n", " │ │ { │ │\n", " │ │ 'index': 1, │ │\n", - " │ │ 'step': 'Cook the macaroni according to the package instructions, then drain and set │ │\n", - " │ │ aside.' │ │\n", + " │ │ 'step': 'Soak the cashews in water for at least 2 hours, then drain.' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 2, │ │\n", - " │ │ 'step': 'In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, │ │\n", - " │ │ garlic powder, onion powder, turmeric (if using), and salt (if using). Blend until smooth.' │ │\n", + " │ │ 'step': 'Cook the macaroni according to the package instructions, then drain.' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 3, │ │\n", - " │ │ 'step': 'Pour the sauce over the cooked macaroni and stir to combine.' │ │\n", + " │ │ 'step': 'In a blender, combine the soaked cashews, nutritional yeast, garlic powder, onion │ │\n", + " │ │ powder, turmeric (if using), salt, pepper (if using), and water. Blend until smooth.' │ │\n", " │ │ }, │ │\n", " │ │ { │ │\n", " │ │ 'index': 4, │ │\n", - " │ │ 'step': 'Serve the vegan mac and cheese hot, with additional nutritional yeast or other │ │\n", - " │ │ toppings if desired.' │ │\n", - " │ │ } │ │\n", + " │ │ 'step': 'Pour the sauce over the cooked macaroni and stir to combine.' │ │\n", + " │ │ }, │ │\n", + " │ │ {'index': 5, 'step': 'Serve the vegan mac and cheese hot.'} │ │\n", " │ │ ] │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -818,26 +811,25 @@ " │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -861,102 +853,105 @@ " │ \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 \"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\": 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\": \"ounces\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"almond milk\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Silk\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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\": 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\": \"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\": \"Bragg\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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.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 \"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\": 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\": \"tahini\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"tablespoons\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"lemon juice\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"tablespoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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\": 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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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\": 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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"turmeric\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 \"quantity\": 0.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 \"units\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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\": 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\": 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 \"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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"instructions\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"step\": \"Cook the macaroni according to the package instructions, then drain and set \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220maside.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"step\": \"In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgarlic powder, onion powder, turmeric (if using), and salt (if using). Blend until 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\": 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\": \"Pour the sauce over the cooked macaroni and stir to combine.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Serve the vegan mac and cheese hot, with additional nutritional yeast or other \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mtoppings if desired.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"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 \"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,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"ounces\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"cashews\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Whole Foods\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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,\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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\": \"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 \"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.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 \"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\": 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\": \"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 \"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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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 \"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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"turmeric\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Simply Organic\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": 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 \"quantity\": 0.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 \"units\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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 \"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\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"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 \"optional\": 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 \"quantity\": 0.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 \"units\": \"teaspoon\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"water\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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.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\": \"cups\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"instructions\": [\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"step\": \"Soak the cashews in water for at least 2 hours, then drain.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"step\": \"Cook the macaroni according to the package instructions, then drain.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 blender, combine the soaked cashews, nutritional yeast, garlic powder, onion \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mpowder, turmeric (if using), salt, pepper (if using), and water. Blend until 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\": 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\": \"Pour the sauce over the cooked macaroni and stir to combine.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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\": \"Serve the vegan mac and cheese hot.\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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", @@ -965,15 +960,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': 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': '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 '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 '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': 'ounces'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'almond milk',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'Silk',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'cashews',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'Whole Foods',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '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", @@ -981,80 +976,79 @@ " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m {\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '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': 'Bragg',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 '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.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 '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': 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': 'tahini',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 '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 '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': 'tablespoons'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'lemon juice',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 '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 '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': 'tablespoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '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': '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 'name': 'turmeric',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'Simply Organic',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 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 'quantity': 0.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 'units': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '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': '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 '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 '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': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'turmeric',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 'name': '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 'optional': 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 'quantity': 0.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 'units': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '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': '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': 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 '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': 'teaspoon'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'water',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': '',\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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.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': 'cups'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 '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': 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 'step': 'Cook the macaroni according to the package instructions, then drain and set \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240maside.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'Soak the cashews in water for at least 2 hours, then drain.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 'step': 'In a blender, combine the almond milk, nutritional yeast, tahini, lemon juice, \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mgarlic powder, onion powder, turmeric (if using), and salt (if using). Blend until 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 'step': 'Cook the macaroni according to the package instructions, then drain.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 'step': 'Pour the sauce over the cooked macaroni and stir to combine.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 blender, combine the soaked cashews, nutritional yeast, garlic powder, onion \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mpowder, turmeric (if using), salt, pepper (if using), and water. Blend until 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': 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': 'Serve the vegan mac and cheese hot, with additional nutritional yeast or other \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", - " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240mtoppings if desired.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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 'step': 'Pour the sauce over the cooked macaroni and stir to combine.'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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, 'step': 'Serve the vegan mac and cheese hot.'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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", @@ -1086,7 +1080,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/regex_validation.ipynb b/docs/examples/regex_validation.ipynb index 3d9f948d7..cf6866892 100644 --- a/docs/examples/regex_validation.ipynb +++ b/docs/examples/regex_validation.ipynb @@ -2,11 +2,22 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mregex_match...\u001b[0m\n", + "✅Successfully installed guardrails/regex_match!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/regex_match" + "!guardrails hub install hub://guardrails/regex_match --quiet" ] }, { @@ -29,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -45,9 +56,18 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ "import openai\n", "from guardrails import Guard\n", @@ -66,16 +86,9 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 5, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - }, { "data": { "text/html": [ @@ -92,10 +105,10 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ Sure! Here's a fake phone number for your movie: 555-123-4567. │ │\n", + " │ │ 555-789-1234 │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ \"Sure! Here's a fake phone number for your movie: 555-123-4567.\" │ │\n", + " │ │ '555-789-1234' │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "\n" @@ -114,10 +127,10 @@ " │ \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;220mSure! Here's a fake phone number for your movie: 555-123-4567.\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m555-789-1234\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[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\"Sure! Here's a fake phone number for your movie: 555-123-4567.\"\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[0m │\n", + " │ \u001b[48;2;240;255;240m│\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m'555-789-1234'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[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" ] @@ -156,7 +169,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/examples/response_is_on_topic.ipynb b/docs/examples/response_is_on_topic.ipynb index 58692a146..5f007b66b 100644 --- a/docs/examples/response_is_on_topic.ipynb +++ b/docs/examples/response_is_on_topic.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -10,25 +10,15 @@ "output_type": "stream", "text": [ "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/tryolabs/\u001b[0m\u001b[95mrestricttotopic...\u001b[0m\n", - "\u001b[2K\u001b[32m[ ===]\u001b[0m Fetching manifestst\n", - "\u001b[2K\u001b[32m[== ]\u001b[0m Downloading dependencies Running command git clone --filter=blob:none --quiet https://github.com/tryolabs/restricttotopic.git /private/var/folders/w2/ssf16z690zd7_4dggw0y5s_m0000gn/T/pip-req-build-advwvzw9\n", - "\u001b[2K\u001b[32m[=== ]\u001b[0m Downloading dependencies\n", - "\u001b[1A\u001b[2K\u001b[?25l\u001b[32m[ ]\u001b[0m Running post-install setup\n", - "\u001b[1A\u001b[2K✅Successfully installed tryolabs/restricttotopic!\n", - "\n", + "✅Successfully installed tryolabs/restricttotopic!\n", "\n", - "\u001b[1mImport validator:\u001b[0m\n", - "from guardrails.hub import RestrictToTopic\n", - "\n", - "\u001b[1mGet more info:\u001b[0m\n", - "\u001b[4;94mhttps://hub.guardrailsai.com/validator/tryolabs/restricttotopic\u001b[0m\n", "\n" ] } ], "source": [ "\n", - "!guardrails hub install hub://tryolabs/restricttotopic" + "!guardrails hub install hub://tryolabs/restricttotopic --quiet" ] }, { @@ -68,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -85,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -107,7 +97,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -146,9 +136,17 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + }, { "name": "stdout", "output_type": "stream", @@ -197,7 +195,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -243,14 +241,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Validation failed for field with errors: Invalid topics found: ['tablet']\n" + "Validation failed for field with errors: Invalid topics found: ['tablet', 'phone']\n" ] } ], @@ -295,7 +293,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/secrets_detection.ipynb b/docs/examples/secrets_detection.ipynb index 3be7b0905..d26753353 100644 --- a/docs/examples/secrets_detection.ipynb +++ b/docs/examples/secrets_detection.ipynb @@ -2,11 +2,22 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95msecrets_present...\u001b[0m\n", + "✅Successfully installed guardrails/secrets_present!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/secrets_present" + "!guardrails hub install hub://guardrails/secrets_present --quiet" ] }, { @@ -42,15 +53,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages/torch/cuda/__init__.py:611: UserWarning: Can't initialize NVML\n", - " warnings.warn(\"Can't initialize NVML\")\n" + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" ] } ], @@ -65,7 +76,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -81,7 +92,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -154,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -237,7 +248,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/select_choice_based_on_action.ipynb b/docs/examples/select_choice_based_on_action.ipynb index a26124879..acec7c5cd 100644 --- a/docs/examples/select_choice_based_on_action.ipynb +++ b/docs/examples/select_choice_based_on_action.ipynb @@ -2,11 +2,22 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mvalid_choices...\u001b[0m\n", + "✅Successfully installed guardrails/valid_choices!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/valid_choices" + "!guardrails hub install hub://guardrails/valid_choices --quiet" ] }, { @@ -43,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 24, "metadata": { "ExecuteTime": { "end_time": "2023-08-23T15:09:26.331177Z", @@ -72,7 +83,7 @@ "\n", "You run into a ${opp_type}. What do you do?\n", "\n", - "${gr.complete_json_suffix_v2}\n", + "${gr.complete_xml_suffix_v2}\n", "\n", "\n", "\"\"\"" @@ -87,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -100,7 +111,7 @@ "\n", "You run into a ${opp_type}. What do you do?\n", "\n", - "${gr.complete_json_suffix_v2}\"\"\"\n", + "${gr.complete_xml_suffix_v2}\"\"\"\n", "\n", "class Fight(BaseModel):\n", " chosen_action: Literal['fight']\n", @@ -138,7 +149,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 26, "metadata": { "ExecuteTime": { "end_time": "2023-08-23T15:09:28.590929Z", @@ -163,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -171,19 +182,99 @@ "\n", "from rich import print\n", "\n", - "guard = gd.Guard.from_pydantic(output_class=FightOrFlight, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=FightOrFlight)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Guard` object compiles the output schema and adds it to the 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:" + "## Step 3: Wrap the LLM API call with `Guard`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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": 14, + "execution_count": 28, + "metadata": { + "ExecuteTime": { + "end_time": "2023-08-23T15:10:08.998121Z", + "start_time": "2023-08-23T15:10:08.792027Z" + } + }, + "outputs": [], + "source": [ + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={'opp_type': 'giant'},\n", + " model=\"gpt-3.5-turbo\",\n", + " max_tokens=256,\n", + " temperature=0.0,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Running the cell above returns:\n", + "1. The raw LLM text output as a single string.\n", + "2. A dictionary where the key is `python_code` and the value is the generated code.\n", + "\n", + "We can see that if the LLM chooses `flight`, the output is a dictionary with `flight_direction` and `distance` fields." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
{'action': {'chosen_action': 'flight', 'flight_direction': 'north', 'distance': 3}}\n",
+       "
\n" + ], + "text/plain": [ + "\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;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(validated_response)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also see the final prompt below:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, "metadata": { "ExecuteTime": { "end_time": "2023-08-23T15:09:32.364711Z", @@ -198,24 +289,25 @@ "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 giant. 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", - " <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", + " <choice discriminator=\"chosen_action\" name=\"action\" required=\"true\">\n", + " <case name=\"fight\">\n", + " <string format=\"guardrails/valid_choices: ['crossbow', 'machine gun']\" name=\"weapon\" \n", + "required=\"true\"></string>\n", + " </case>\n", + " <case name=\"flight\">\n", + " <string format=\"guardrails/valid_choices: ['north', 'south', 'east', 'west']\" name=\"flight_direction\" \n", + "required=\"true\"></string>\n", + " <integer format=\"guardrails/valid_choices: [1, 2, 3, 4]\" name=\"distance\" required=\"true\"></integer>\n", + " </case>\n", + " </choice>\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", @@ -234,24 +326,25 @@ "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 giant. 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\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\n", + "\u001b[39m \u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\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\u001b[35m/\u001b[0m\u001b[95mstring\u001b[0m\u001b[39m>\u001b[0m\n", + "\u001b[39m <\u001b[0m\u001b[35m/\u001b[0m\u001b[95minteger\u001b[0m\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", "\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", @@ -270,93 +363,7 @@ } ], "source": [ - "print(guard.base_prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "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": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": 15, - "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": [ - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={'opp_type': 'giant'},\n", - " model=\"gpt-3.5-turbo\",\n", - " max_tokens=256,\n", - " temperature=0.0,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Running the cell above returns:\n", - "1. The raw LLM text output as a single string.\n", - "2. A dictionary where the key is `python_code` and the value is the generated code.\n", - "\n", - "We can see that if the LLM chooses `flight`, the output is a dictionary with `flight_direction` and `distance` fields." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
{'action': {'chosen_action': 'flight', 'flight_direction': 'north', 'distance': 1}}\n",
-       "
\n" - ], - "text/plain": [ - "\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": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(validated_response)" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -368,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -388,19 +395,20 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\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", - " │ │ </case> │ │\n", - " │ │ </choice> │ │\n", + " │ │ <choice discriminator=\"chosen_action\" name=\"action\" required=\"true\"> │ │\n", + " │ │ <case name=\"fight\"> │ │\n", + " │ │ <string format=\"guardrails/valid_choices: ['crossbow', 'machine gun']\" name=\"weapon\" │ │\n", + " │ │ required=\"true\"></string> │ │\n", + " │ │ </case> │ │\n", + " │ │ <case name=\"flight\"> │ │\n", + " │ │ <string format=\"guardrails/valid_choices: ['north', 'south', 'east', 'west']\" │ │\n", + " │ │ name=\"flight_direction\" required=\"true\"></string> │ │\n", + " │ │ <integer format=\"guardrails/valid_choices: [1, 2, 3, 4]\" name=\"distance\" │ │\n", + " │ │ required=\"true\"></integer> │ │\n", + " │ │ </case> │ │\n", + " │ │ </choice> │ │\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 │ │\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", @@ -422,20 +430,14 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ { │ │\n", - " │ │ \"action\": { │ │\n", - " │ │ \"chosen_action\": \"flight\", │ │\n", - " │ │ \"flight_direction\": \"north\", │ │\n", - " │ │ \"distance\": 1 │ │\n", - " │ │ } │ │\n", - " │ │ } │ │\n", + " │ │ {\"action\":{\"chosen_action\":\"flight\",\"flight_direction\":\"north\",\"distance\":3}} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ { │ │\n", " │ │ 'action': { │ │\n", " │ │ 'chosen_action': 'flight', │ │\n", " │ │ 'flight_direction': 'north', │ │\n", - " │ │ 'distance': 1 │ │\n", + " │ │ 'distance': 3 │ │\n", " │ │ } │ │\n", " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", @@ -457,19 +459,20 @@ " │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -491,20 +494,14 @@ " │ \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 \"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\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"action\":{\"chosen_action\":\"flight\",\"flight_direction\":\"north\",\"distance\":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 │\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 '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 'distance': 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 }\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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", @@ -530,21 +527,13 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 32, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - } - ], + "outputs": [], "source": [ "raw_llm_response, validated_response, *rest = guard(\n", " openai.chat.completions.create,\n", + " prompt=prompt,\n", " prompt_params={'opp_type': 'goblin'},\n", " model=\"gpt-3.5-turbo\",\n", " max_tokens=256,\n", @@ -554,7 +543,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -584,7 +573,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -604,19 +593,20 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\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", - " │ │ </case> │ │\n", - " │ │ </choice> │ │\n", + " │ │ <choice discriminator=\"chosen_action\" name=\"action\" required=\"true\"> │ │\n", + " │ │ <case name=\"fight\"> │ │\n", + " │ │ <string format=\"guardrails/valid_choices: ['crossbow', 'machine gun']\" name=\"weapon\" │ │\n", + " │ │ required=\"true\"></string> │ │\n", + " │ │ </case> │ │\n", + " │ │ <case name=\"flight\"> │ │\n", + " │ │ <string format=\"guardrails/valid_choices: ['north', 'south', 'east', 'west']\" │ │\n", + " │ │ name=\"flight_direction\" required=\"true\"></string> │ │\n", + " │ │ <integer format=\"guardrails/valid_choices: [1, 2, 3, 4]\" name=\"distance\" │ │\n", + " │ │ required=\"true\"></integer> │ │\n", + " │ │ </case> │ │\n", + " │ │ </choice> │ │\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 │ │\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", @@ -638,12 +628,7 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ { │ │\n", - " │ │ \"action\": { │ │\n", - " │ │ \"chosen_action\": \"fight\", │ │\n", - " │ │ \"weapon\": \"crossbow\" │ │\n", - " │ │ } │ │\n", - " │ │ } │ │\n", + " │ │ {\"action\":{\"chosen_action\":\"fight\",\"weapon\":\"crossbow\"}} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ {'action': {'chosen_action': 'fight', 'weapon': 'crossbow'}} │ │\n", @@ -666,19 +651,20 @@ " │ \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\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -700,12 +686,7 @@ " │ \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 \"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\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m{\"action\":{\"chosen_action\":\"fight\",\"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 │\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': {'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", @@ -738,7 +719,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/syntax_error_free_sql.ipynb b/docs/examples/syntax_error_free_sql.ipynb index 30adf2121..af9336556 100644 --- a/docs/examples/syntax_error_free_sql.ipynb +++ b/docs/examples/syntax_error_free_sql.ipynb @@ -2,11 +2,22 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mvalid_sql...\u001b[0m\n", + "✅Successfully installed guardrails/valid_sql!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/valid_sql" + "!guardrails hub install hub://guardrails/valid_sql --quiet" ] }, { @@ -32,22 +43,11 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: sqlvalidator in /home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages (0.0.20)\n", - "\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.3.1\u001b[0m\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" - ] - } - ], + "outputs": [], "source": [ - "! pip install sqlvalidator" + "! pip install sqlvalidator -q" ] }, { @@ -73,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -96,7 +96,7 @@ "\n", "${nl_instruction}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\n", @@ -113,15 +113,15 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/zayd/workspace/guardrails/.venv/lib/python3.9/site-packages/torch/cuda/__init__.py:611: UserWarning: Can't initialize NVML\n", - " warnings.warn(\"Can't initialize NVML\")\n" + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" ] } ], @@ -135,7 +135,7 @@ "\n", "${nl_instruction}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "class ValidSql(BaseModel):\n", @@ -167,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -185,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -201,11 +201,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=ValidSql, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=ValidSql)" ] }, { @@ -213,12 +213,46 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We see the prompt that will be sent to the LLM:" + "Here, `nl_language` is the natural language instruction and will be provided by the user at runtime." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wrap the LLM API call with `Guard`" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={\n", + " \"nl_instruction\": \"Select the name of the employee who has the highest salary.\"\n", + " },\n", + " max_tokens=2048,\n", + " temperature=0,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see the prompt that was sent to the LLM:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -230,26 +264,7 @@ "\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", - "format=\"bug-free-sql\"/>\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 `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\" format=\"1-indexed\" \n", - "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n", - "\n", + "${gr.complete_xml_suffix}\n", "\n", "\n" ], @@ -260,26 +275,7 @@ "\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\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 `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[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[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", + "$\u001b[1m{\u001b[0mgr.complete_xml_suffix\u001b[1m}\u001b[0m\n", "\n" ] }, @@ -288,49 +284,7 @@ } ], "source": [ - "print(guard.base_prompt)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, `nl_language` is the natural language instruction and will be provided by the user at runtime." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={\n", - " \"nl_instruction\": \"Select the name of the employee who has the highest salary.\"\n", - " },\n", - " max_tokens=2048,\n", - " temperature=0,\n", - ")" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -344,17 +298,17 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
{'generated_sql': 'SELECT name FROM employee ORDER BY salary DESC LIMIT 1'}\n",
+       "
{'generated_sql': 'SELECT name FROM employees WHERE salary = (SELECT MAX(salary) FROM employees)'}\n",
        "
\n" ], "text/plain": [ - "\u001b[1m{\u001b[0m\u001b[32m'generated_sql'\u001b[0m: \u001b[32m'SELECT name FROM employee ORDER BY salary DESC LIMIT 1'\u001b[0m\u001b[1m}\u001b[0m\n" + "\u001b[1m{\u001b[0m\u001b[32m'generated_sql'\u001b[0m: \u001b[32m'SELECT name FROM employees WHERE salary = \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSELECT MAX\u001b[0m\u001b[32m(\u001b[0m\u001b[32msalary\u001b[0m\u001b[32m)\u001b[0m\u001b[32m FROM employees\u001b[0m\u001b[32m)\u001b[0m\u001b[32m'\u001b[0m\u001b[1m}\u001b[0m\n" ] }, "metadata": {}, @@ -367,7 +321,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -387,11 +341,10 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"generated_sql\" description=\"Generate SQL for the given natural language instruction.\" │ │\n", - " │ │ format=\"bug-free-sql\"/> │ │\n", + " │ │ <string description=\"Generate SQL for the given natural language instruction.\" │ │\n", + " │ │ format=\"guardrails/valid_sql: None None\" name=\"generated_sql\" required=\"true\"></string> │ │\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 │ │\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", @@ -415,12 +368,12 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ { │ │\n", - " │ │ \"generated_sql\": \"SELECT name FROM employee ORDER BY salary DESC LIMIT 1\" │ │\n", - " │ │ } │ │\n", + " │ │ {\"generated_sql\":\"SELECT name FROM employees WHERE salary = (SELECT MAX(salary) FROM employees)\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ {'generated_sql': 'SELECT name FROM employee ORDER BY salary DESC LIMIT 1'} │ │\n", + " │ │ { │ │\n", + " │ │ 'generated_sql': 'SELECT name FROM employees WHERE salary = (SELECT MAX(salary) FROM employees)' │ │\n", + " │ │ } │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "
\n" @@ -440,11 +393,10 @@ " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -468,12 +420,12 @@ " │ \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 \"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\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 employees WHERE salary = (SELECT MAX(salary) FROM employees)\"}\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[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{'generated_sql': 'SELECT name FROM employee ORDER BY salary DESC LIMIT 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 'generated_sql': 'SELECT name FROM employees WHERE salary = (SELECT MAX(salary) FROM employees)'\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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" ] @@ -503,7 +455,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/text_summarization_quality.ipynb b/docs/examples/text_summarization_quality.ipynb index b9c7e59ed..b97c1e193 100644 --- a/docs/examples/text_summarization_quality.ipynb +++ b/docs/examples/text_summarization_quality.ipynb @@ -2,44 +2,22 @@ "cells": [ { "cell_type": "code", - "execution_count": 14, + "execution_count": 2, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...\n", - "To disable this warning, you can either:\n", - "\t- Avoid using `tokenizers` before the fork if possible\n", - "\t- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95msimilar_to_document...\u001b[0m\n", - "\u001b[2K\u001b[32m[= ]\u001b[0m Fetching manifestst\n", - "\u001b[2K\u001b[32m[== ]\u001b[0m Downloading dependenciespendencies Running command git clone --filter=blob:none --quiet https://github.com/guardrails-ai/similar_to_document.git /private/var/folders/w2/ssf16z690zd7_4dggw0y5s_m0000gn/T/pip-req-build-oys8q6q2\n", - "\u001b[2K\u001b[32m[=== ]\u001b[0m Downloading dependencies\u001b[33mWARNING: Target directory /Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/hub/guardrails/similar_to_document/validator already exists. Specify --upgrade to force replacement.\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33mWARNING: Target directory /Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/guardrails/hub/guardrails/similar_to_document/similar_to_document-0.0.0.dist-info already exists. Specify --upgrade to force replacement.\u001b[0m\u001b[33m\n", - "\u001b[2K\u001b[32m[ ==]\u001b[0m Downloading dependencies\n", - "\u001b[1A\u001b[2K\u001b[?25l\u001b[32m[ ]\u001b[0m Running post-install setup\n", - "\u001b[1A\u001b[2K✅Successfully installed guardrails/similar_to_document!\n", - "\n", - "\n", - "\u001b[1mImport validator:\u001b[0m\n", - "from guardrails.hub import SimilarToDocument\n", + "✅Successfully installed guardrails/similar_to_document!\n", "\n", - "\u001b[1mGet more info:\u001b[0m\n", - "\u001b[4;94mhttps://hub.guardrailsai.com/validator/guardrails/similar_to_document\u001b[0m\n", "\n" ] } ], "source": [ - "!guardrails hub install hub://guardrails/similar_to_document" + "!guardrails hub install hub://guardrails/similar_to_document --quiet" ] }, { @@ -65,11 +43,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "!pip install numpy" + "! pip install numpy -q" ] }, { @@ -95,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -114,7 +92,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -138,7 +116,7 @@ "\n", "${document}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\"\"\"\n", @@ -154,9 +132,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading the model all-MiniLM-L6-v2. This may take a while...\n" + ] + } + ], "source": [ "from pydantic import BaseModel, Field\n", "\n", @@ -167,7 +153,7 @@ "\n", "${document}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "\n", @@ -205,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -223,7 +209,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -239,135 +225,11 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loading the model all-MiniLM-L6-v2. This may take a while...\n" - ] - } - ], - "source": [ - "guard = gd.Guard.from_pydantic(output_class=DocumentSummary, prompt=prompt)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see the prompt that will be sent to the LLM:" - ] - }, - { - "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n",
-       "Summarize the following document:\n",
-       "\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",
-       "\n",
-       "<output>\n",
-       "    <string name=\"summary\" description=\"Summarize the given document faithfully.\" \n",
-       "format=\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted shall be vested \n",
-       "in a Congress of the United States, which shall consist of a Senate and House of Representatives.Section. 2.The \n",
-       "House of Representatives shall be composed of Members chosen every second Year by the People of the several States,\n",
-       "and the Electors in each State shall have the Qualifications requisite for Electors of the most numerous Branch of \n",
-       "the State Legislature.No Person shall be a Representative who shall not have attained to the Age of twenty five \n",
-       "Years, and been seven Years a Citizen of the United States, and who shall not, when elected, be an Inhabitant of \n",
-       "that State in which he shall be chosen.Representatives and direct Taxes shall be apportioned among the several \n",
-       "States which may be included within this Union, according to their respective Numbers, which shall be determined by\n",
-       "adding to the whole Number of free Persons, including those bound to Service for a Term of Years, and excluding \n",
-       "Indians not taxed, three fifths of all other Persons. The actual Enumeration shall be made within three Years after\n",
-       "the first Meeting of the Congress of the United States, and within every subsequent Term of ten Years, in such \n",
-       "Manner as they shall by Law direct. The Number of Representatives shall not exceed one for every thirty Thousand, \n",
-       "but each State shall have at Least one Representative; and until such enumeration shall be made, the State of New \n",
-       "Hampshire shall be entitled to chuse three, Massachusetts eight, Rhode-Island and Providence Plantations one, \n",
-       "Connecticut five, New-York six, New Jersey four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, \n",
-       "North Carolina five, South Carolina five, and Georgia three.When vacancies happen in the Representation from any \n",
-       "State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies.The House of \n",
-       "Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of Impeachment.' \n",
-       "threshold=0.6 model=all-MiniLM-L6-v2\"/>\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 `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\" format=\"1-indexed\" \n",
-       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
-       "\n",
-       "\n",
-       "
\n" - ], - "text/plain": [ - "\n", - "Summarize the following document:\n", - "\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", - "\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[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 `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[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[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" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ - "print(guard.rail.prompt)" + "guard = gd.Guard.from_pydantic(output_class=DocumentSummary)" ] }, { @@ -393,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -456,6 +318,7 @@ "\n", "raw_llm_response, validated_response, *rest = guard(\n", " openai.chat.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\"document\": document},\n", " model=\"gpt-3.5-turbo\",\n", " max_tokens=2048,\n", @@ -465,6 +328,176 @@ "print(f\"Validated Output: {validated_response}\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see the prompt that was sent to the LLM:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n",
+       "Summarize the following document:\n",
+       "\n",
+       "Section. 1.\n",
+       "All legislative Powers herein granted shall be vested in a Congress of the United States, which shall consist of a \n",
+       "Senate and House of Representatives.\n",
+       "\n",
+       "Section. 2.\n",
+       "The House of Representatives shall be composed of Members chosen every second Year by the People of the several \n",
+       "States, and the Electors in each State shall have the Qualifications requisite for Electors of the most numerous \n",
+       "Branch of the State Legislature.\n",
+       "\n",
+       "No Person shall be a Representative who shall not have attained to the Age of twenty five Years, and been seven \n",
+       "Years a Citizen of the United States, and who shall not, when elected, be an Inhabitant of that State in which he \n",
+       "shall be chosen.\n",
+       "\n",
+       "Representatives and direct Taxes shall be apportioned among the several States which may be included within this \n",
+       "Union, according to their respective Numbers, which shall be determined by adding to the whole Number of free \n",
+       "Persons, including those bound to Service for a Term of Years, and excluding Indians not taxed, three fifths of all\n",
+       "other Persons. The actual Enumeration shall be made within three Years after the first Meeting of the Congress of \n",
+       "the United States, and within every subsequent Term of ten Years, in such Manner as they shall by Law direct. The \n",
+       "Number of Representatives shall not exceed one for every thirty Thousand, but each State shall have at Least one \n",
+       "Representative; and until such enumeration shall be made, the State of New Hampshire shall be entitled to chuse \n",
+       "three, Massachusetts eight, Rhode-Island and Providence Plantations one, Connecticut five, New-York six, New Jersey\n",
+       "four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, North Carolina five, South Carolina five, and \n",
+       "Georgia three.\n",
+       "\n",
+       "When vacancies happen in the Representation from any State, the Executive Authority thereof shall issue Writs of \n",
+       "Election to fill such Vacancies.\n",
+       "\n",
+       "The House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of \n",
+       "Impeachment.\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 description=\"Summarize the given document faithfully.\" format=\"guardrails/similar_to_document: 'Section. \n",
+       "1.All legislative Powers herein granted shall be vested in a Congress of the United States, which shall consist of \n",
+       "a Senate and House of Representatives.Section. 2.The House of Representatives shall be composed of Members chosen \n",
+       "every second Year by the People of the several States, and the Electors in each State shall have the Qualifications\n",
+       "requisite for Electors of the most numerous Branch of the State Legislature.No Person shall be a Representative who\n",
+       "shall not have attained to the Age of twenty five Years, and been seven Years a Citizen of the United States, and \n",
+       "who shall not, when elected, be an Inhabitant of that State in which he shall be chosen.Representatives and direct \n",
+       "Taxes shall be apportioned among the several States which may be included within this Union, according to their \n",
+       "respective Numbers, which shall be determined by adding to the whole Number of free Persons, including those bound \n",
+       "to Service for a Term of Years, and excluding Indians not taxed, three fifths of all other Persons. The actual \n",
+       "Enumeration shall be made within three Years after the first Meeting of the Congress of the United States, and \n",
+       "within every subsequent Term of ten Years, in such Manner as they shall by Law direct. The Number of \n",
+       "Representatives shall not exceed one for every thirty Thousand, but each State shall have at Least one \n",
+       "Representative; and until such enumeration shall be made, the State of New Hampshire shall be entitled to chuse \n",
+       "three, Massachusetts eight, Rhode-Island and Providence Plantations one, Connecticut five, New-York six, New Jersey\n",
+       "four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, North Carolina five, South Carolina five, and \n",
+       "Georgia three.When vacancies happen in the Representation from any State, the Executive Authority thereof shall \n",
+       "issue Writs of Election to fill such Vacancies.The House of Representatives shall chuse their Speaker and other \n",
+       "Officers; and shall have the sole Power of Impeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" \n",
+       "required=\"true\"></string>\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. 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\" format=\"1-indexed\" \n",
+       "/></object>` => `{'baz': {'foo': 'Some String', 'index': 1}}`\n",
+       "\n",
+       "\n",
+       "
\n" + ], + "text/plain": [ + "\n", + "Summarize the following document:\n", + "\n", + "Section. \u001b[1;36m1\u001b[0m.\n", + "All legislative Powers herein granted shall be vested in a Congress of the United States, which shall consist of a \n", + "Senate and House of Representatives.\n", + "\n", + "Section. \u001b[1;36m2\u001b[0m.\n", + "The House of Representatives shall be composed of Members chosen every second Year by the People of the several \n", + "States, and the Electors in each State shall have the Qualifications requisite for Electors of the most numerous \n", + "Branch of the State Legislature.\n", + "\n", + "No Person shall be a Representative who shall not have attained to the Age of twenty five Years, and been seven \n", + "Years a Citizen of the United States, and who shall not, when elected, be an Inhabitant of that State in which he \n", + "shall be chosen.\n", + "\n", + "Representatives and direct Taxes shall be apportioned among the several States which may be included within this \n", + "Union, according to their respective Numbers, which shall be determined by adding to the whole Number of free \n", + "Persons, including those bound to Service for a Term of Years, and excluding Indians not taxed, three fifths of all\n", + "other Persons. The actual Enumeration shall be made within three Years after the first Meeting of the Congress of \n", + "the United States, and within every subsequent Term of ten Years, in such Manner as they shall by Law direct. The \n", + "Number of Representatives shall not exceed one for every thirty Thousand, but each State shall have at Least one \n", + "Representative; and until such enumeration shall be made, the State of New Hampshire shall be entitled to chuse \n", + "three, Massachusetts eight, Rhode-Island and Providence Plantations one, Connecticut five, New-York six, New Jersey\n", + "four, Pennsylvania eight, Delaware one, Maryland six, Virginia ten, North Carolina five, South Carolina five, and \n", + "Georgia three.\n", + "\n", + "When vacancies happen in the Representation from any State, the Executive Authority thereof shall issue Writs of \n", + "Election to fill such Vacancies.\n", + "\n", + "The House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of \n", + "Impeachment.\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\u001b[35m/\u001b[0m\u001b[95mstring\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. 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[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[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" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "print(guard.history.last.compiled_prompt)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -475,7 +508,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -523,9 +556,9 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"summary\" description=\"Summarize the given document faithfully.\" │ │\n", - " │ │ format=\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted │ │\n", - " │ │ shall be vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", + " │ │ <string description=\"Summarize the given document faithfully.\" │ │\n", + " │ │ format=\"guardrails/similar_to_document: 'Section. 1.All legislative Powers herein granted shall be │ │\n", + " │ │ vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", " │ │ Representatives.Section. 2.The House of Representatives shall be composed of Members chosen every │ │\n", " │ │ second Year by the People of the several States, and the Electors in each State shall have the │ │\n", " │ │ Qualifications requisite for Electors of the most numerous Branch of the State Legislature.No Person │ │\n", @@ -544,10 +577,9 @@ " │ │ Carolina five, South Carolina five, and Georgia three.When vacancies happen in the Representation from │ │\n", " │ │ any State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies.The │ │\n", " │ │ House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of │ │\n", - " │ │ Impeachment.' threshold=0.6 model=all-MiniLM-L6-v2\"/> │ │\n", + " │ │ Impeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\"></string> │ │\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 │ │\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", @@ -662,9 +694,9 @@ " │ \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;255mImpeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\">\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -778,7 +809,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -798,6 +829,7 @@ "source": [ "raw_llm_response, validated_response, *rest = guard(\n", " openai.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\"document\": open(\"data/article1.txt\", \"r\").read()},\n", " model=\"babbage-002\",\n", " max_tokens=512,\n", @@ -817,7 +849,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -865,9 +897,9 @@ "│ │ │ it into. │ │\n", "│ │ │ │ │\n", "│ │ │ <output> │ │\n", - "│ │ │ <string name=\"summary\" description=\"Summarize the given document faithfully.\" │ │\n", - "│ │ │ format=\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted │ │\n", - "│ │ │ shall be vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", + "│ │ │ <string description=\"Summarize the given document faithfully.\" │ │\n", + "│ │ │ format=\"guardrails/similar_to_document: 'Section. 1.All legislative Powers herein granted shall be │ │\n", + "│ │ │ vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", "│ │ │ Representatives.Section. 2.The House of Representatives shall be composed of Members chosen every │ │\n", "│ │ │ second Year by the People of the several States, and the Electors in each State shall have the │ │\n", "│ │ │ Qualifications requisite for Electors of the most numerous Branch of the State Legislature.No Person │ │\n", @@ -886,10 +918,9 @@ "│ │ │ Carolina five, South Carolina five, and Georgia three.When vacancies happen in the Representation from │ │\n", "│ │ │ any State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies.The │ │\n", "│ │ │ House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of │ │\n", - "│ │ │ Impeachment.' threshold=0.6 model=all-MiniLM-L6-v2\"/> │ │\n", + "│ │ │ Impeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\"></string> │ │\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 │ │\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", @@ -913,58 +944,20 @@ "│ │ │ No message history. │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", "│ │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - "│ │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - "│ │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - "│ │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - "│ │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - "│ │ │ of Representatives.', │ │\n", - "│ │ │ \"name\": \"summary\", │ │\n", - "│ │ │ \"threshold\": 0.6, │ │\n", - "│ │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - "│ │ │ } │ │\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", - "│ │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - "│ │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - "│ │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - "│ │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - "│ │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - "│ │ │ of Representatives.', │ │\n", - "│ │ │ \"name\": \"summary\", │ │\n", - "│ │ │ \"threshold\": 0.6, │ │\n", - "│ │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - "│ │ │ } │ │\n", "│ │ │ │ │\n", "│ │ │ JSON Output: │ │\n", "│ │ │ │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - "│ │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - "│ │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - "│ │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - "│ │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - "│ │ │ of Representatives.', │ │\n", - "│ │ │ \"name\": \"summary\", │ │\n", - "│ │ │ \"threshold\": 0.6, │ │\n", - "│ │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - "│ │ │ } │ │\n", + "│ │ │ - `{\"foo\": \"example one\"}` │ │\n", + "│ │ │ - `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}` │ │\n", + "│ │ │ - `{\"baz\": {'foo': 'Some String', 'index': 1}}` │ │\n", "│ │ │ │ │\n", - "│ │ │ JSON Output: │ │\n", - "│ │ │ │ │\n", - "│ │ │ { │ │\n", - "│ │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - "│ │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - "│ │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - "│ │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - "│ │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - "│ │ │ of Representatives.', │ │\n", - "│ │ │ \"name\": \"summary\", │ │\n", - "│ │ │ \"threshold\": 0.6, │ │\n", - "│ │ │ \"model\": \"all-M │ │\n", "│ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", "│ │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", "│ │ │ None │ │\n", @@ -975,32 +968,12 @@ " │ │ │ │\n", " │ │ I was given the following response, which was not parseable as JSON. │ │\n", " │ │ │ │\n", - " │ │ \" {\\n \\\"summary\\\": \\\"Summarize the given document faithfully.\\\",\\n \\\"string\\\": │ │\n", - " │ │ \\\"Section. 1.All legislative Powers herein granted shall be vested in a Congress of the United States, │ │\n", - " │ │ which shall consist of a Senate and House of Representatives.\\\",\\n \\\"format\\\": │ │\n", - " │ │ \\\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted shall be │ │\n", - " │ │ vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", - " │ │ Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \\\"threshold\\\": 0.6,\\n \\\"model\\\": │ │\n", - " │ │ \\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n \\\"summary\\\": \\\"Summarize the given │ │\n", - " │ │ document faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All legislative Powers herein granted shall │ │\n", - " │ │ be vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", - " │ │ Representatives.\\\",\\n \\\"format\\\": \\\"guardrails/similar_to_document: document='Section. 1.All │ │\n", - " │ │ legislative Powers herein granted shall be vested in a Congress of the United States, which shall │ │\n", - " │ │ consist of a Senate and House of Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n │ │\n", - " │ │ \\\"threshold\\\": 0.6,\\n \\\"model\\\": \\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n │ │\n", - " │ │ \\\"summary\\\": \\\"Summarize the given document faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All │ │\n", - " │ │ legislative Powers herein granted shall be vested in a Congress of the United States, which shall │ │\n", - " │ │ consist of a Senate and House of Representatives.\\\",\\n \\\"format\\\": │ │\n", - " │ │ \\\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted shall be │ │\n", - " │ │ vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", - " │ │ Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \\\"threshold\\\": 0.6,\\n \\\"model\\\": │ │\n", - " │ │ \\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n \\\"summary\\\": \\\"Summarize the given │ │\n", - " │ │ document faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All legislative Powers herein granted shall │ │\n", - " │ │ be vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", - " │ │ Representatives.\\\",\\n \\\"format\\\": \\\"guardrails/similar_to_document: document='Section. 1.All │ │\n", - " │ │ legislative Powers herein granted shall be vested in a Congress of the United States, which shall │ │\n", - " │ │ consist of a Senate and House of Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n │ │\n", - " │ │ \\\"threshold\\\": 0.6,\\n \\\"model\\\": \\\"all-M\" │ │\n", + " │ │ \"- `<string name='foo' format='two-words lower-case' />` => `{'foo': 'example one'}`\\n- `<list │ │\n", + " │ │ name='bar'><string format='upper-case' /></list>` => `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n- │ │\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\\nJSON │ │\n", + " │ │ Output:\\n\\n- `{\\\"foo\\\": \\\"example one\\\"}`\\n- `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n- │ │\n", + " │ │ `{\\\"baz\\\": {'foo': 'Some String', 'index': 1}}`\\n\" │ │\n", " │ │ │ │\n", " │ │ Help me correct this by making it valid JSON. │ │\n", " │ │ │ │\n", @@ -1008,9 +981,9 @@ " │ │ it into. │ │\n", " │ │ │ │\n", " │ │ <output> │ │\n", - " │ │ <string name=\"summary\" description=\"Summarize the given document faithfully.\" │ │\n", - " │ │ format=\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted │ │\n", - " │ │ shall be vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", + " │ │ <string description=\"Summarize the given document faithfully.\" │ │\n", + " │ │ format=\"guardrails/similar_to_document: 'Section. 1.All legislative Powers herein granted shall be │ │\n", + " │ │ vested in a Congress of the United States, which shall consist of a Senate and House of │ │\n", " │ │ Representatives.Section. 2.The House of Representatives shall be composed of Members chosen every │ │\n", " │ │ second Year by the People of the several States, and the Electors in each State shall have the │ │\n", " │ │ Qualifications requisite for Electors of the most numerous Branch of the State Legislature.No Person │ │\n", @@ -1029,10 +1002,9 @@ " │ │ Carolina five, South Carolina five, and Georgia three.When vacancies happen in the Representation from │ │\n", " │ │ any State, the Executive Authority thereof shall issue Writs of Election to fill such Vacancies.The │ │\n", " │ │ House of Representatives shall chuse their Speaker and other Officers; and shall have the sole Power of │ │\n", - " │ │ Impeachment.' threshold=0.6 model=all-MiniLM-L6-v2\"/> │ │\n", + " │ │ Impeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\"></string> │ │\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 │ │\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", @@ -1048,58 +1020,10 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ { │ │\n", - " │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - " │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - " │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - " │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - " │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - " │ │ of Representatives.', │ │\n", - " │ │ \"name\": \"summary\", │ │\n", - " │ │ \"threshold\": 0.6, │ │\n", - " │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - " │ │ } │ │\n", - " │ │ │ │\n", - " │ │ JSON Output: │ │\n", - " │ │ │ │\n", - " │ │ { │ │\n", - " │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - " │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - " │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - " │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - " │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - " │ │ of Representatives.', │ │\n", - " │ │ \"name\": \"summary\", │ │\n", - " │ │ \"threshold\": 0.6, │ │\n", - " │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - " │ │ } │ │\n", - " │ │ │ │\n", - " │ │ JSON Output: │ │\n", + " │ │ - `{\\\"foo\\\": \\\"example one\\\"}`\\n- `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n- `{\\\"baz\\\": {'foo': │ │\n", + " │ │ 'Some String', 'index': 1}}`\\n │ │\n", " │ │ │ │\n", - " │ │ { │ │\n", - " │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - " │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - " │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - " │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - " │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - " │ │ of Representatives.', │ │\n", - " │ │ \"name\": \"summary\", │ │\n", - " │ │ \"threshold\": 0.6, │ │\n", - " │ │ \"model\": \"all-MiniLM-L6-v2\" │ │\n", - " │ │ } │ │\n", " │ │ │ │\n", - " │ │ JSON Output: │ │\n", - " │ │ │ │\n", - " │ │ { │ │\n", - " │ │ \"summary\": \"Summarize the given document faithfully.\", │ │\n", - " │ │ \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress of │ │\n", - " │ │ the United States, which shall consist of a Senate and House of Representatives.\", │ │\n", - " │ │ \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein │ │\n", - " │ │ granted shall be vested in a Congress of the United States, which shall consist of a Senate and House │ │\n", - " │ │ of Representatives.', │ │\n", - " │ │ \"name\": \"summary\", │ │\n", - " │ │ \"threshold\": 0.6, │ │\n", - " │ │ \"model\": \"all-M │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ None │ │\n", @@ -1150,9 +1074,9 @@ "│ │ \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;255mImpeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\">\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", "│ │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1198,58 +1121,20 @@ "│ │ \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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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- `` => `{'foo': 'example one'}`\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m- `` => `{\"bar\": ['STRING ONE', 'STRING TWO', \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220metc.]}`\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m- `` => `{'baz': {'foo': 'Some String', '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 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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- `{\"foo\": \"example one\"}`\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m- `{\"bar\": ['STRING ONE', 'STRING TWO', etc.]}`\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m- `{\"baz\": {'foo': 'Some String', '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 \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - "│ │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-M\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[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", @@ -1260,32 +1145,12 @@ " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 response, which was not parseable as JSON.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\" {\\n \\\"summary\\\": \\\"Summarize the given document faithfully.\\\",\\n \\\"string\\\": \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"Section. 1.All legislative Powers herein granted shall be vested in a Congress of the United States, \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mwhich shall consist of a Senate and House of Representatives.\\\",\\n \\\"format\\\": \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted shall be \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mvested in a Congress of the United States, which shall consist of a Senate and House of \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mRepresentatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \\\"threshold\\\": 0.6,\\n \\\"model\\\": \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n \\\"summary\\\": \\\"Summarize the given \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All legislative Powers herein granted shall \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mbe vested in a Congress of the United States, which shall consist of a Senate and House of \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mRepresentatives.\\\",\\n \\\"format\\\": \\\"guardrails/similar_to_document: document='Section. 1.All \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mlegislative Powers herein granted shall be vested in a Congress of the United States, which shall \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mconsist of a Senate and House of Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"threshold\\\": 0.6,\\n \\\"model\\\": \\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"summary\\\": \\\"Summarize the given document faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mlegislative Powers herein granted shall be vested in a Congress of the United States, which shall \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mconsist of a Senate and House of Representatives.\\\",\\n \\\"format\\\": \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein granted shall be \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mvested in a Congress of the United States, which shall consist of a Senate and House of \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mRepresentatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \\\"threshold\\\": 0.6,\\n \\\"model\\\": \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"all-MiniLM-L6-v2\\\"\\n }\\n\\nJSON Output:\\n\\n {\\n \\\"summary\\\": \\\"Summarize the given \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 faithfully.\\\",\\n \\\"string\\\": \\\"Section. 1.All legislative Powers herein granted shall \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mbe vested in a Congress of the United States, which shall consist of a Senate and House of \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mRepresentatives.\\\",\\n \\\"format\\\": \\\"guardrails/similar_to_document: document='Section. 1.All \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mlegislative Powers herein granted shall be vested in a Congress of the United States, which shall \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mconsist of a Senate and House of Representatives.',\\n \\\"name\\\": \\\"summary\\\",\\n \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\\\"threshold\\\": 0.6,\\n \\\"model\\\": \\\"all-M\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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'}`\\n- `` => `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n-\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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}}`\\n\\n\\n\\nJSON \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", + " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mOutput:\\n\\n- `{\\\"foo\\\": \\\"example one\\\"}`\\n- `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n- \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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}}`\\n\"\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\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 this by making it valid JSON.\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1293,9 +1158,9 @@ " │ \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;255mImpeachment.' 0.6 all-MiniLM-L6-v2\" name=\"summary\" required=\"true\">\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", - " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -1333,58 +1197,10 @@ " │ \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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-MiniLM-L6-v2\"\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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- `{\\\"foo\\\": \\\"example one\\\"}`\\n- `{\\\"bar\\\": ['STRING ONE', 'STRING TWO', etc.]}`\\n- `{\\\"baz\\\": {'foo':\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", + " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m'Some String', 'index': 1}}`\\n\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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;220mJSON Output:\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[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 \"summary\": \"Summarize the given document faithfully.\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"string\": \"Section. 1.All legislative Powers herein granted shall be vested in a Congress 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;220mthe United States, which shall consist of a Senate and House 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;220m \"format\": \"guardrails/similar_to_document: document='Section. 1.All legislative Powers herein \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220mgranted shall be vested in a Congress of the United States, which shall consist of a Senate and House \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\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 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;220m \"name\": \"summary\",\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m│\u001b[0m │\n", - " │ \u001b[48;2;245;245;220m│\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[48;2;245;245;220m \"threshold\": 0.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 \"model\": \"all-M\u001b[0m\u001b[48;2;245;245;220m \u001b[0m\u001b[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", @@ -1417,7 +1233,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/examples/toxic_language.ipynb b/docs/examples/toxic_language.ipynb index 78dbe51e9..b5b0fb36c 100644 --- a/docs/examples/toxic_language.ipynb +++ b/docs/examples/toxic_language.ipynb @@ -2,11 +2,24 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95mtoxic_language...\u001b[0m\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", + " warnings.warn(\n", + "✅Successfully installed guardrails/toxic_language!\n", + "\n", + "\n" + ] + } + ], "source": [ - "!guardrails hub install hub://guardrails/toxic_language" + "! guardrails hub install hub://guardrails/toxic_language --quiet" ] }, { @@ -22,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -38,7 +51,16 @@ "cell_type": "code", "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", + " warnings.warn(\n" + ] + } + ], "source": [ "# Create a Guard object with this validator\n", "# Here, we'll use the default validation method of \"sentence\"\n", @@ -208,7 +230,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.17" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/translation_to_specific_language.ipynb b/docs/examples/translation_to_specific_language.ipynb index c8dacdc39..97ed817d7 100644 --- a/docs/examples/translation_to_specific_language.ipynb +++ b/docs/examples/translation_to_specific_language.ipynb @@ -23,39 +23,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting alt-profanity-check\n", - " Downloading alt_profanity_check-1.5.0.tar.gz (758 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m759.0/759.0 kB\u001b[0m \u001b[31m7.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n", - "\u001b[?25h Installing build dependencies ... \u001b[?25ldone\n", - "\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n", - "\u001b[?25h Installing backend dependencies ... \u001b[?25ldone\n", - "\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n", - "\u001b[?25hRequirement already satisfied: scikit-learn==1.5.0 in ./.venv/lib/python3.10/site-packages (from alt-profanity-check) (1.5.0)\n", - "Requirement already satisfied: joblib>=1.4.0 in ./.venv/lib/python3.10/site-packages (from alt-profanity-check) (1.4.2)\n", - "Requirement already satisfied: numpy>=1.19.5 in ./.venv/lib/python3.10/site-packages (from scikit-learn==1.5.0->alt-profanity-check) (1.26.4)\n", - "Requirement already satisfied: scipy>=1.6.0 in ./.venv/lib/python3.10/site-packages (from scikit-learn==1.5.0->alt-profanity-check) (1.13.1)\n", - "Requirement already satisfied: threadpoolctl>=3.1.0 in ./.venv/lib/python3.10/site-packages (from scikit-learn==1.5.0->alt-profanity-check) (3.5.0)\n", - "Building wheels for collected packages: alt-profanity-check\n", - " Building wheel for alt-profanity-check (pyproject.toml) ... \u001b[?25ldone\n", - "\u001b[?25h Created wheel for alt-profanity-check: filename=alt_profanity_check-1.5.0-py3-none-any.whl size=758311 sha256=e0f54f82189ad2c90aeb27cb9239175c71d38606836be9e4762fb64b2e2de0a0\n", - " Stored in directory: /Users/wyatt/Library/Caches/pip/wheels/18/c3/20/637574a9badb43cace85202ca31f49f47e3fe65e076459f3ed\n", - "Successfully built alt-profanity-check\n", - "Installing collected packages: alt-profanity-check\n", - "Successfully installed alt-profanity-check-1.5.0\n" - ] - } - ], + "outputs": [], "source": [ - "! pip install alt-profanity-check" + "! pip install alt-profanity-check --quiet" ] }, { @@ -80,30 +54,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/wyatt/Projects/guardrails/docs/examples/.venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n", - "/Users/wyatt/Projects/guardrails/guardrails/validators/__init__.py:51: FutureWarning: \n", - " Importing validators from `guardrails.validators` is deprecated.\n", - " All validators are now available in the Guardrails Hub. Please install\n", - " and import them from the hub instead. All validators will be\n", - " removed from this module in the next major release.\n", - "\n", - " Install with: `guardrails hub install hub:///`\n", - " Import as: from guardrails.hub import `ValidatorName`\n", - " \n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "from profanity_check import predict\n", "from guardrails.validators import (\n", @@ -139,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": { "tags": [] }, @@ -163,7 +118,7 @@ "\n", "${statement_to_be_translated}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\n", @@ -179,23 +134,9 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/wyatt/Projects/guardrails/guardrails/validator_base.py:460: FutureWarning: Accessing `IsProfanityFree` using\n", - "`from guardrails.validators import IsProfanityFree` is deprecated and\n", - "support will be removed after version 0.5.x. Please switch to the Guardrails Hub syntax:\n", - "`from guardrails.hub import ProfanityFree` for future updates and support.\n", - "For additional details, please visit: https://hub.guardrailsai.com/validator/guardrails/profanity_free.\n", - "\n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "from pydantic import BaseModel, Field\n", "\n", @@ -204,7 +145,7 @@ "\n", "${statement_to_be_translated}\n", "\n", - "${gr.complete_json_suffix}\n", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "\n", @@ -239,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -257,20 +198,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/wyatt/Projects/guardrails/guardrails/prompt/base_prompt.py:59: FutureWarning: Prompt Primitives are moving! To keep the same behaviour, switch from `json` constants to `xml` constants. Example: ${gr.complete_json_suffix} -> ${gr.complete_xml_suffix}\n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "guard = gd.Guard.from_rail_string(rail_str)" ] @@ -284,68 +216,99 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=Translation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, `statement_to_be_translated` is the the statement and will be provided by the user at runtime." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wrap the LLM API call with `Guard`" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, let's try translating a statement that doesn't have any profanity in it." + ] + }, + { + "cell_type": "code", + "execution_count": 9, "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/wyatt/Projects/guardrails/guardrails/validator_base.py:460: FutureWarning: Accessing `IsProfanityFree` using\n", - "`from guardrails.validators import IsProfanityFree` is deprecated and\n", - "support will be removed after version 0.5.x. Please switch to the Guardrails Hub syntax:\n", - "`from guardrails.hub import ProfanityFree` for future updates and support.\n", - "For additional details, please visit: https://hub.guardrailsai.com/validator/guardrails/profanity_free.\n", - "\n", - " warn(\n", - "/Users/wyatt/Projects/guardrails/guardrails/prompt/base_prompt.py:59: FutureWarning: Prompt Primitives are moving! To keep the same behaviour, switch from `json` constants to `xml` constants. Example: ${gr.complete_json_suffix} -> ${gr.complete_xml_suffix}\n", - " warn(\n" - ] + "data": { + "text/html": [ + "
Validated Output: {'translated_statement': 'chicken quesadilla'}\n",
+       "
\n" + ], + "text/plain": [ + "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'translated_statement'\u001b[0m: \u001b[32m'chicken quesadilla'\u001b[0m\u001b[1m}\u001b[0m\n" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "guard = gd.Guard.from_pydantic(output_class=Translation, prompt=prompt)" + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={\"statement_to_be_translated\": \"quesadilla de pollo\"},\n", + " model=\"gpt-3.5-turbo\",\n", + " max_tokens=2048,\n", + " temperature=0,\n", + ")\n", + "\n", + "print(f\"Validated Output: {validated_response}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We see the prompt that will be sent to the LLM:" + "We can see the prompt that was sent to the LLM:" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": { "tags": [] }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/var/folders/8n/8qwytjb11kj_46_w3n2v4jzw0000gn/T/ipykernel_6330/3983563700.py:1: DeprecationWarning: 'Guard.base_prompt' is deprecated and will be removed in versions 0.5.x and beyond. Use 'Guard.history.last.prompt' instead.\n", - " print(guard.base_prompt)\n" - ] - }, { "data": { "text/html": [ "
\n",
        "Translate the given statement into english language:\n",
        "\n",
-       "${statement_to_be_translated}\n",
+       "quesadilla de pollo\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 description=\"Translate the given statement into english language\" format=\"is-profanity-free\" \n",
+       "name=\"translated_statement\" required=\"true\"></string>\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",
@@ -364,17 +327,16 @@
        "\n",
        "Translate the given statement into english language:\n",
        "\n",
-       "$\u001b[1m{\u001b[0mstatement_to_be_translated\u001b[1m}\u001b[0m\n",
+       "quesadilla de pollo\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\u001b[35m/\u001b[0m\u001b[95mstring\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[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",
@@ -394,74 +356,19 @@
     }
    ],
    "source": [
-    "print(guard.base_prompt)"
+    "print(guard.history.last.compiled_prompt)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Here, `statement_to_be_translated` is the the statement and will be provided by the user at runtime."
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Step 3: Wrap the LLM API call with `Guard`"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "First, let's try translating a statement that doesn't have any profanity in it."
+    "We can also take a look at the output of the LLM and the validated output using the Guard's internal logs:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "
Validated Output: {'translated_statement': 'Chicken quesadilla'}\n",
-       "
\n" - ], - "text/plain": [ - "Validated Output: \u001b[1m{\u001b[0m\u001b[32m'translated_statement'\u001b[0m: \u001b[32m'Chicken quesadilla'\u001b[0m\u001b[1m}\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={\"statement_to_be_translated\": \"quesadilla de pollo\"},\n", - " model=\"gpt-3.5-turbo\",\n", - " max_tokens=2048,\n", - " temperature=0,\n", - ")\n", - "\n", - "print(f\"Validated Output: {validated_response}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can take a look at the output of the LLM and the validated output using the Guard's internal logs:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -480,11 +387,10 @@ " │ │ 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 description=\"Translate the given statement into english language\" format=\"is-profanity-free\" │ │\n", + " │ │ name=\"translated_statement\" required=\"true\"></string> │ │\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 │ │\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", @@ -508,10 +414,10 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ {\"translated_statement\":\"Chicken quesadilla\"} │ │\n", + " │ │ {\"translated_statement\":\"chicken quesadilla\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", - " │ │ {'translated_statement': 'Chicken quesadilla'} │ │\n", + " │ │ {'translated_statement': 'chicken quesadilla'} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\n", "
\n" @@ -530,11 +436,10 @@ " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -558,10 +463,10 @@ " │ \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{\"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\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", " │ \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': 'Chicken quesadilla'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[48;2;240;255;240m│\u001b[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': 'chicken quesadilla'}\u001b[0m\u001b[48;2;240;255;240m \u001b[0m\u001b[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" ] @@ -586,7 +491,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 13, "metadata": { "tags": [] }, @@ -608,6 +513,7 @@ "source": [ "raw_llm_response, validated_response, *rest = guard(\n", " openai.chat.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\"statement_to_be_translated\": \"убей себя\"},\n", " model=\"gpt-3.5-turbo\",\n", " max_tokens=2048,\n", @@ -627,7 +533,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -646,11 +552,10 @@ " │ │ 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 description=\"Translate the given statement into english language\" format=\"is-profanity-free\" │ │\n", + " │ │ name=\"translated_statement\" required=\"true\"></string> │ │\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 │ │\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", @@ -674,7 +579,7 @@ " │ │ No message history. │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭──────────────────────────────────────────── Raw LLM Output ─────────────────────────────────────────────╮ │\n", - " │ │ {\"translated_statement\":\"Kill yourself\"} │ │\n", + " │ │ {\"translated_statement\":\"kill yourself\"} │ │\n", " │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │\n", " │ ╭─────────────────────────────────────────── Validated Output ────────────────────────────────────────────╮ │\n", " │ │ {'translated_statement': ''} │ │\n", @@ -696,11 +601,10 @@ " │ \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", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mONLY 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;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[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` attribute of the corresponding XML, and the value is of the type specified by the corresponding \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", " │ \u001b[48;2;240;248;255m│\u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255mXML's tag. The JSON MUST conform to the XML format, including any types and format requests e.g. \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m \u001b[0m\u001b[48;2;240;248;255m│\u001b[0m │\n", @@ -724,7 +628,7 @@ " │ \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{\"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\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", " │ \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", @@ -757,7 +661,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.3" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/docs/examples/valid_chess_moves.ipynb b/docs/examples/valid_chess_moves.ipynb index 57980d31d..676784d72 100644 --- a/docs/examples/valid_chess_moves.ipynb +++ b/docs/examples/valid_chess_moves.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -32,19 +32,11 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 2, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: chess in /home/zayd/workspace/shreya/guardrails/.venv/lib/python3.11/site-packages (1.10.0)\n" - ] - } - ], + "outputs": [], "source": [ - "! pip install chess" + "! pip install chess --quiet" ] }, { @@ -66,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -106,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -121,7 +113,7 @@ "\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", + "${gr.complete_xml_suffix}\n", "\n", "\n", "\n", @@ -137,7 +129,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +138,7 @@ "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", + "${gr.complete_xml_suffix}\n", "\"\"\"\n", "\n", "class ChessMove(BaseModel):\n", @@ -178,7 +170,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -194,11 +186,79 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "guard = gd.Guard.from_pydantic(output_class=ChessMove)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's get the reference to the board." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "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": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "board = guard._validator_map.get(\"$.move\")[0].board\n", + "board" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Step 3: Wrap the LLM API call with `Guard`" + ] + }, + { + "cell_type": "code", + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=ChessMove, prompt=prompt)" + "import openai\n", + "\n", + "raw_llm_response, validated_response, *rest = guard(\n", + " openai.chat.completions.create,\n", + " prompt=prompt,\n", + " prompt_params={\n", + " \"board_state\": str(board.move_stack)\n", + " if board.move_stack\n", + " else \"Starting position.\"\n", + " },\n", + " model=\"gpt-3.5-turbo\",\n", + " max_tokens=2048,\n", + " temperature=0.3,\n", + ")" ] }, { @@ -206,12 +266,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We see the prompt that will be sent to the LLM. The `{board_state}` is substituted with the current state of the board." + "We can see in the prompt that was sent to the LLM, the `{board_state}` parameter is substituted with the current state of the board." ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -219,15 +279,15 @@ "text/html": [ "
\n",
        "Generate a move for the chess board. The board is currently in the following state:\n",
-       "${board_state}\n",
+       "Starting position.\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=\"move\" format=\"is-valid-chess-move\" description=\"A move in standard algebraic notation.\"/>\n",
+       "  <string description=\"A move in standard algebraic notation.\" format=\"is-valid-chess-move\" name=\"move\" \n",
+       "required=\"true\"></string>\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",
@@ -245,15 +305,15 @@
       "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",
+       "Starting position.\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\u001b[35m/\u001b[0m\u001b[95mstring\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[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",
@@ -273,82 +333,7 @@
     }
    ],
    "source": [
-    "print(guard.base_prompt)"
-   ]
-  },
-  {
-   "attachments": {},
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Let's get the reference to the board."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 73,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "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": 73, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "board = guard.output_schema.root_datatype.children.__getattribute__(\"move\").validators[0].board\n", - "board" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step 3: Wrap the LLM API call with `Guard`" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "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" - ] - } - ], - "source": [ - "import openai\n", - "\n", - "raw_llm_response, validated_response, *rest = guard(\n", - " openai.chat.completions.create,\n", - " prompt_params={\n", - " \"board_state\": str(board.move_stack)\n", - " if board.move_stack\n", - " else \"Starting position.\"\n", - " },\n", - " model=\"gpt-3.5-turbo\",\n", - " max_tokens=2048,\n", - " temperature=0.3,\n", - ")" + "print(guard.history.last.compiled_prompt)" ] }, { @@ -362,7 +347,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -385,7 +370,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -404,7 +389,7 @@ "Board('rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1')" ] }, - "execution_count": 76, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -423,7 +408,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -442,7 +427,7 @@ "Board('rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2')" ] }, - "execution_count": 77, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -462,21 +447,13 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 26, "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", - "Async event loop found, but guard was invoked synchronously.For validator parallelization, please call `validate_async` instead.\n" - ] - } - ], + "outputs": [], "source": [ "raw_llm_response, validated_response, *rest = guard(\n", " openai.chat.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\n", " \"board_state\": str(board.move_stack)\n", " if board.move_stack\n", @@ -490,7 +467,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -509,7 +486,7 @@ "Board('rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2')" ] }, - "execution_count": 79, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -520,25 +497,32 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 29, "metadata": {}, "outputs": [ { - "ename": "IllegalMoveError", - "evalue": "illegal san: 'Nc6' in rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mIllegalMoveError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[80], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m board\u001b[39m.\u001b[39;49mpush_san(\u001b[39m\"\u001b[39;49m\u001b[39mNc6\u001b[39;49m\u001b[39m\"\u001b[39;49m)\n\u001b[1;32m 2\u001b[0m board\n", - "File \u001b[0;32m~/workspace/shreya/guardrails/.venv/lib/python3.11/site-packages/chess/__init__.py:3105\u001b[0m, in \u001b[0;36mBoard.push_san\u001b[0;34m(self, san)\u001b[0m\n\u001b[1;32m 3091\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mpush_san\u001b[39m(\u001b[39mself\u001b[39m, san: \u001b[39mstr\u001b[39m) \u001b[39m-\u001b[39m\u001b[39m>\u001b[39m Move:\n\u001b[1;32m 3092\u001b[0m \u001b[39m \u001b[39m\u001b[39m\"\"\"\u001b[39;00m\n\u001b[1;32m 3093\u001b[0m \u001b[39m Parses a move in standard algebraic notation, makes the move and puts\u001b[39;00m\n\u001b[1;32m 3094\u001b[0m \u001b[39m it onto the move stack.\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 3103\u001b[0m \u001b[39m - :exc:`AmbiguousMoveError` if the SAN is ambiguous.\u001b[39;00m\n\u001b[1;32m 3104\u001b[0m \u001b[39m \"\"\"\u001b[39;00m\n\u001b[0;32m-> 3105\u001b[0m move \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mparse_san(san)\n\u001b[1;32m 3106\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mpush(move)\n\u001b[1;32m 3107\u001b[0m \u001b[39mreturn\u001b[39;00m move\n", - "File \u001b[0;32m~/workspace/shreya/guardrails/.venv/lib/python3.11/site-packages/chess/__init__.py:3087\u001b[0m, in \u001b[0;36mBoard.parse_san\u001b[0;34m(self, san)\u001b[0m\n\u001b[1;32m 3084\u001b[0m matched_move \u001b[39m=\u001b[39m move\n\u001b[1;32m 3086\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m matched_move:\n\u001b[0;32m-> 3087\u001b[0m \u001b[39mraise\u001b[39;00m IllegalMoveError(\u001b[39mf\u001b[39m\u001b[39m\"\u001b[39m\u001b[39millegal san: \u001b[39m\u001b[39m{\u001b[39;00msan\u001b[39m!r}\u001b[39;00m\u001b[39m in \u001b[39m\u001b[39m{\u001b[39;00m\u001b[39mself\u001b[39m\u001b[39m.\u001b[39mfen()\u001b[39m}\u001b[39;00m\u001b[39m\"\u001b[39m)\n\u001b[1;32m 3089\u001b[0m \u001b[39mreturn\u001b[39;00m matched_move\n", - "\u001b[0;31mIllegalMoveError\u001b[0m: illegal san: 'Nc6' in rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2" - ] + "data": { + "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 . B Q K B N R
" + ], + "text/plain": [ + "Board('rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR b KQkq - 1 2')" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "board.push_san(\"Nc6\")\n", + "board.push_san(\"Nc3\")\n", "board" ] } @@ -559,7 +543,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.12.3" }, "orig_nbformat": 4, "vscode": { diff --git a/docs/examples/value_within_distribution.ipynb b/docs/examples/value_within_distribution.ipynb index 15192ed00..dfe97532f 100644 --- a/docs/examples/value_within_distribution.ipynb +++ b/docs/examples/value_within_distribution.ipynb @@ -10,26 +10,16 @@ "output_type": "stream", "text": [ "Installing hub:\u001b[35m/\u001b[0m\u001b[35m/guardrails/\u001b[0m\u001b[95msimilar_to_previous_values...\u001b[0m\n", - "\u001b[2K\u001b[32m[= ]\u001b[0m Fetching manifestst\n", - "\u001b[2K\u001b[32m[== ]\u001b[0m Downloading dependencies Running command git clone --filter=blob:none --quiet https://github.com/guardrails-ai/similar_to_previous_values.git /private/var/folders/w2/ssf16z690zd7_4dggw0y5s_m0000gn/T/pip-req-build-8nvu7jlq\n", - "\u001b[2K\u001b[32m[ ===]\u001b[0m Downloading dependencies\n", - "\u001b[2K\u001b[32m[ ===]\u001b[0m Running post-install setuptall setup/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.11/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.\n", " warnings.warn(\n", - "\u001b[2K\u001b[32m[ ==]\u001b[0m Running post-install setup\n", - "\u001b[1A\u001b[2K✅Successfully installed guardrails/similar_to_previous_values!\n", + "✅Successfully installed guardrails/similar_to_previous_values!\n", "\n", - "\n", - "\u001b[1mImport validator:\u001b[0m\n", - "from guardrails.hub import SimilarToPreviousValues\n", - "\n", - "\u001b[1mGet more info:\u001b[0m\n", - "\u001b[4;94mhttps://hub.guardrailsai.com/validator/guardrails/similar_to_previous_values\u001b[0m\n", "\n" ] } ], "source": [ - "!guardrails hub install hub://guardrails/similar_to_previous_values" + "!guardrails hub install hub://guardrails/similar_to_previous_values --quiet" ] }, { @@ -49,9 +39,18 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/calebcourier/Projects/gr-mono/guardrails/docs/examples/.venv/lib/python3.12/site-packages/sentence_transformers/cross_encoder/CrossEncoder.py:11: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from tqdm.autonotebook import tqdm, trange\n" + ] + } + ], "source": [ "# Create the Guard with the SimilarToList validator\n", "from typing import Union\n", @@ -75,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -105,7 +104,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -142,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -167,19 +166,9 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 7, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -204,19 +193,9 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 8, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n", - "HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -256,7 +235,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.3" }, "orig_nbformat": 4 }, diff --git a/docs/guardrails_ai/getting_started.md b/docs/guardrails_ai/getting_started.md index c6499a365..c82a8c7c9 100644 --- a/docs/guardrails_ai/getting_started.md +++ b/docs/guardrails_ai/getting_started.md @@ -82,12 +82,13 @@ import openai prompt = """ What kind of pet should I get and what should I name it? - ${gr.complete_json_suffix_v2} + ${gr.complete_xml_suffix_v2} """ -guard = Guard.from_pydantic(output_class=Pet, prompt=prompt) +guard = Guard.from_pydantic(output_class=Pet) raw_output, validated_output, *rest = guard( llm_api=openai.chat.completions.create, + prompt=prompt, engine="gpt-3.5-turbo" ) diff --git a/docs/how_to_guides/logs.md b/docs/how_to_guides/logs.md index 13b4bc1ea..a503625af 100644 --- a/docs/how_to_guides/logs.md +++ b/docs/how_to_guides/logs.md @@ -56,7 +56,7 @@ You are a human in an enchanted forest. You come across opponents of different t You run into a ${opp_type}. What do you do? -${gr.complete_json_suffix_v2} +${gr.complete_xml_suffix_v2} Here are a few examples diff --git a/docs/how_to_guides/streaming.ipynb b/docs/how_to_guides/streaming.ipynb index 1e3254120..0c7920b14 100644 --- a/docs/how_to_guides/streaming.ipynb +++ b/docs/how_to_guides/streaming.ipynb @@ -116,7 +116,7 @@ "\n", "${doctors_notes}\n", "\n", - "${gr.complete_json_suffix_v2}\n", + "${gr.complete_xml_suffix_v2}\n", "\"\"\"\n", "\n", "doctors_notes = \"\"\"152 y/o female with chronic macular rash to face and hair, worse in beard, eyebrows and nares.\n", @@ -172,7 +172,7 @@ "metadata": {}, "outputs": [], "source": [ - "guard = gd.Guard.from_pydantic(output_class=PatientInfo, prompt=prompt)" + "guard = gd.Guard.from_pydantic(output_class=PatientInfo)" ] }, { @@ -236,6 +236,7 @@ "# Wrap the OpenAI API call with the `guard` object\n", "raw_llm_output, validated_output, *rest = guard(\n", " openai.chat.completions.create,\n", + " prompt=prompt,\n", " prompt_params={\"doctors_notes\": doctors_notes},\n", " max_tokens=1024,\n", " temperature=0.3,\n", diff --git a/docs/hub/api_reference_markdown/validators.md b/docs/hub/api_reference_markdown/validators.md index 09a9ca7c6..57a2276bb 100644 --- a/docs/hub/api_reference_markdown/validators.md +++ b/docs/hub/api_reference_markdown/validators.md @@ -1,1153 +1,2 @@ # Validators -## TwoWords - -Validates that a value is two words. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `two-words` | -| Supported data types | `string` | -| Programmatic fix | Pick the first two words. | - -## ExtractiveSummary - -Validates that a string is a valid extractive summary of a given -document. - -This validator does a fuzzy match between the sentences in the -summary and the sentences in the document. Each sentence in the -summary must be similar to at least one sentence in the document. -After the validation, the summary is updated to include the -sentences from the document that were matched, and the citations for -those sentences are added to the end of the summary. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `extractive-summary` | -| Supported data types | `string` | -| Programmatic fix | Remove any sentences that can not be verified. | - -**Arguments**: - - -- `threshold` - The minimum fuzz ratio to be considered summarized. Defaults to 85. - - Other parameters: Metadata - -- `filepaths` _List[str]_ - A list of strings that specifies the filepaths for any documents that should be used for asserting the summary's similarity. - -#### validate(value: Any, metadata: Dict) - -```python -def validate(value: Any, metadata: Dict) -> ValidationResult -``` - -Make sure each sentence was precisely copied from the document. - -## SaliencyCheck - -Checks that the summary covers the list of topics present in the -document. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `saliency-check` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - - -- `docs_dir` - Path to the directory containing the documents. -- `threshold` - Threshold for overlap between topics in document and summary. Defaults to 0.25 - -#### \_\_init\_\_(docs\_dir: str, llm\_callable: Optional[Callable] = None, on\_fail: Optional[Callable] = None, threshold: float = 0.25, \*\*kwargs) - -```python -def __init__(docs_dir: str, - llm_callable: Optional[Callable] = None, - on_fail: Optional[Callable] = None, - threshold: float = 0.25, - **kwargs) -``` - -Initialize the SalienceCheck validator. - -**Arguments**: - -- `docs_dir` - Path to the directory containing the documents. -- `on_fail` - Function to call when validation fails. -- `threshold` - Threshold for overlap between topics in document and summary. - -## BugFreeSQL - -Validates that there are no SQL syntactic bugs in the generated code. - -This is a very minimal implementation that uses the Pypi `sqlvalidator` package -to check if the SQL query is valid. You can implement a custom SQL validator -that uses a database connection to check if the query is valid. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `bug-free-sql` | -| Supported data types | `string` | -| Programmatic fix | None | - -## RemoveRedundantSentences - -Removes redundant sentences from a string. - -This validator removes sentences from a string that are similar to -other sentences in the string. This is useful for removing -repetitive sentences from a string. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `remove-redundant-sentences` | -| Supported data types | `string` | -| Programmatic fix | Remove any redundant sentences. | - -**Arguments**: - - -- `threshold` - The minimum fuzz ratio to be considered redundant. Defaults to 70. - -#### validate(value: Any, metadata: Dict) - -```python -def validate(value: Any, metadata: Dict) -> ValidationResult -``` - -Remove redundant sentences from a string. - -## DetectSecrets - -Validates whether the generated code snippet contains any secrets. - -**Key Properties** -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `detect-secrets` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - - None - - This validator uses the detect-secrets library to check whether the generated code - snippet contains any secrets. If any secrets are detected, the validator fails and - returns the generated code snippet with the secrets replaced with asterisks. - Else the validator returns the generated code snippet. - - Following are some caveats: - - Multiple secrets on the same line may not be caught. e.g. - - Minified code - - One-line lists/dictionaries - - Multi-variable assignments - - Multi-line secrets may not be caught. e.g. - - RSA/SSH keys - - -**Example**: - - ```py - - guard = Guard.from_string(validators=[ - DetectSecrets(on_fail=OnFailAction.FIX) - ]) - guard.parse( - llm_output=code_snippet, - ) - ``` - -#### get\_unique\_secrets(value: str) - -```python -def get_unique_secrets(value: str) -> Tuple[Dict[str, Any], List[str]] -``` - -Get unique secrets from the value. - -**Arguments**: - -- `value` _str_ - The generated code snippet. - - -**Returns**: - -- `unique_secrets` _Dict[str, Any]_ - A dictionary of unique secrets and their - line numbers. -- `lines` _List[str]_ - The lines of the generated code snippet. - -#### get\_modified\_value(unique\_secrets: Dict[str, Any], lines: List[str]) - -```python -def get_modified_value(unique_secrets: Dict[str, Any], - lines: List[str]) -> str -``` - -Replace the secrets on the lines with asterisks. - -**Arguments**: - -- `unique_secrets` _Dict[str, Any]_ - A dictionary of unique secrets and their - line numbers. -- `lines` _List[str]_ - The lines of the generated code snippet. - - -**Returns**: - -- `modified_value` _str_ - The generated code snippet with secrets replaced with - asterisks. - -## ValidRange - -Validates that a value is within a range. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `valid-range` | -| Supported data types | `integer`, `float`, `percentage` | -| Programmatic fix | Closest value within the range. | - -**Arguments**: - -- `min` - The inclusive minimum value of the range. -- `max` - The inclusive maximum value of the range. - -#### validate(value: Any, metadata: Dict) - -```python -def validate(value: Any, metadata: Dict) -> ValidationResult -``` - -Validates that a value is within a range. - -## ExtractedSummarySentencesMatch - -Validates that the extracted summary sentences match the original text -by performing a cosine similarity in the embedding space. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `extracted-summary-sentences-match` | -| Supported data types | `string` | -| Programmatic fix | Remove any sentences that can not be verified. | - -**Arguments**: - - -- `threshold` - The minimum cosine similarity to be considered similar. Default to 0.7. - - Other parameters: Metadata - -- `filepaths` _List[str]_ - A list of strings that specifies the filepaths for any documents that should be used for asserting the summary's similarity. -- `document_store` _DocumentStoreBase, optional_ - The document store to use during validation. Defaults to EphemeralDocumentStore. -- `vector_db` _VectorDBBase, optional_ - A vector database to use for embeddings. Defaults to Faiss. -- `embedding_model` _EmbeddingBase, optional_ - The embeddig model to use. Defaults to OpenAIEmbedding. - -## CompetitorCheck - -Validates that LLM-generated text is not naming any competitors from a -given list. - -In order to use this validator you need to provide an extensive list of the -competitors you want to avoid naming including all common variations. - -**Arguments**: - -- `competitors` _List[str]_ - List of competitors you want to avoid naming - -#### exact\_match(text: str, competitors: List[str]) - -```python -def exact_match(text: str, competitors: List[str]) -> List[str] -``` - -Performs exact match to find competitors from a list in a given -text. - -**Arguments**: - -- `text` _str_ - The text to search for competitors. -- `competitors` _list_ - A list of competitor entities to match. - - -**Returns**: - -- `list` - A list of matched entities. - -#### perform\_ner(text: str, nlp) - -```python -def perform_ner(text: str, nlp) -> List[str] -``` - -Performs named entity recognition on text using a provided NLP -model. - -**Arguments**: - -- `text` _str_ - The text to perform named entity recognition on. -- `nlp` - The NLP model to use for entity recognition. - - -**Returns**: - -- `entities` - A list of entities found. - -#### is\_entity\_in\_list(entities: List[str], competitors: List[str]) - -```python -def is_entity_in_list(entities: List[str], competitors: List[str]) -> List -``` - -Checks if any entity from a list is present in a given list of -competitors. - -**Arguments**: - -- `entities` _list_ - A list of entities to check -- `competitors` _list_ - A list of competitor names to match - - -**Returns**: - -- `List` - List of found competitors - -#### validate(value: str, metadata=Dict) - -```python -def validate(value: str, metadata=Dict) -> ValidationResult -``` - -Checks a text to find competitors' names in it. - -While running, store sentences naming competitors and generate a fixed output -filtering out all flagged sentences. - -**Arguments**: - -- `value` _str_ - The value to be validated. -- `metadata` _Dict, optional_ - Additional metadata. Defaults to empty dict. - - -**Returns**: - -- `ValidationResult` - The validation result. - -## BugFreePython - -Validates that there are no Python syntactic bugs in the generated code. - -This validator checks for syntax errors by running `ast.parse(code)`, -and will raise an exception if there are any. -Only the packages in the `python` environment are available to the code snippet. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `bug-free-python` | -| Supported data types | `string` | -| Programmatic fix | None | - -## LowerCase - -Validates that a value is lower case. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `lower-case` | -| Supported data types | `string` | -| Programmatic fix | Convert to lower case. | - -## EndsWith - -Validates that a list ends with a given value. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `ends-with` | -| Supported data types | `list` | -| Programmatic fix | Append the given value to the list. | - -**Arguments**: - -- `end` - The required last element. - -## QARelevanceLLMEval - -Validates that an answer is relevant to the question asked by asking the -LLM to self evaluate. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `qa-relevance-llm-eval` | -| Supported data types | `string` | -| Programmatic fix | None | - -Other parameters: Metadata -question (str): The original question the llm was given to answer. - -## ValidURL - -Validates that a value is a valid URL. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `valid-url` | -| Supported data types | `string` | -| Programmatic fix | None | - -## SqlColumnPresence - -Validates that all columns in the SQL query are present in the schema. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `sql-column-presence` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `cols` - The list of valid columns. - -## ValidChoices - -Validates that a value is within the acceptable choices. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `valid-choices` | -| Supported data types | `all` | -| Programmatic fix | None | - -**Arguments**: - -- `choices` - The list of valid choices. - -#### validate(value: Any, metadata: Dict) - -```python -def validate(value: Any, metadata: Dict) -> ValidationResult -``` - -Validates that a value is within a range. - -## ReadingTime - -Validates that the a string can be read in less than a certain amount of -time. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `reading-time` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - - -- `reading_time` - The maximum reading time in minutes. - -## IsProfanityFree - -Validates that a translated text does not contain profanity language. - -This validator uses the `alt-profanity-check` package to check if a string -contains profanity language. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `is-profanity-free` | -| Supported data types | `string` | -| Programmatic fix | None | - -## ValidLength - -Validates that the length of value is within the expected range. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `length` | -| Supported data types | `string`, `list`, `object` | -| Programmatic fix | If shorter than the minimum, pad with empty last elements. If longer than the maximum, truncate. | - -**Arguments**: - -- `min` - The inclusive minimum length. -- `max` - The inclusive maximum length. - -#### validate(value: Union[str, List], metadata: Dict) - -```python -def validate(value: Union[str, List], metadata: Dict) -> ValidationResult -``` - -Validates that the length of value is within the expected range. - -## SimilarToList - -Validates that a value is similar to a list of previously known values. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `similar-to-list` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `standard_deviations` _int_ - The number of standard deviations from the mean to check. -- `threshold` _float_ - The threshold for the average semantic similarity for strings. - - For integer values, this validator checks whether the value lies - within 'k' standard deviations of the mean of the previous values. - (Assumes that the previous values are normally distributed.) For - string values, this validator checks whether the average semantic - similarity between the generated value and the previous values is - less than a threshold. - -#### get\_semantic\_similarity(text1: str, text2: str, embed\_function: Callable) - -```python -def get_semantic_similarity(text1: str, text2: str, - embed_function: Callable) -> float -``` - -Get the semantic similarity between two strings. - -**Arguments**: - -- `text1` _str_ - The first string. -- `text2` _str_ - The second string. -- `embed_function` _Callable_ - The embedding function. - -**Returns**: - -- `similarity` _float_ - The semantic similarity between the two strings. - -## PydanticFieldValidator - -Validates a specific field in a Pydantic model with the specified -validator method. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `pydantic_field_validator` | -| Supported data types | `Any` | -| Programmatic fix | Override with return value from `field_validator`. | - -**Arguments**: - - -- `field_validator` _Callable_ - A validator for a specific field in a Pydantic model. - -## PIIFilter - -Validates that any text does not contain any PII. - -This validator uses Microsoft's Presidio (https://github.com/microsoft/presidio) -to detect PII in the text. If PII is detected, the validator will fail with a -programmatic fix that anonymizes the text. Otherwise, the validator will pass. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `pii` | -| Supported data types | `string` | -| Programmatic fix | Anonymized text with PII filtered | - -**Arguments**: - -- `pii_entities` _str | List[str], optional_ - The PII entities to filter. Must be - one of `pii` or `spi`. Defaults to None. Can also be set in metadata. - -#### get\_anonymized\_text(text: str, entities: List[str]) - -```python -def get_anonymized_text(text: str, entities: List[str]) -> str -``` - -Analyze and anonymize the text for PII. - -**Arguments**: - -- `text` _str_ - The text to analyze. -- `pii_entities` _List[str]_ - The PII entities to filter. - - -**Returns**: - -- `anonymized_text` _str_ - The anonymized text. - -## OnTopic - -Checks if text's main topic is specified within a list of valid topics -and ensures that the text is not about any of the invalid topics. - -This validator accepts at least one valid topic and an optional list of -invalid topics. - -Default behavior first runs a Zero-Shot model, and then falls back to -ask OpenAI's `gpt-3.5-turbo` if the Zero-Shot model is not confident -in the topic classification (score < 0.5). - -In our experiments this LLM fallback increases accuracy by 15% but also -increases latency (more than doubles the latency in the worst case). - -Both the Zero-Shot classification and the GPT classification may be toggled. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ---------------------------------------- | -| Name for `format` attribute | `on_topic` | -| Supported data types | `string` | -| Programmatic fix | Removes lines with off-topic information | - -**Arguments**: - -- `valid_topics` _List[str]_ - topics that the text should be about - (one or many). -- `invalid_topics` _List[str], Optional, defaults to []_ - topics that the - text cannot be about. -- `device` _int, Optional, defaults to -1_ - Device ordinal for CPU/GPU - supports for Zero-Shot classifier. Setting this to -1 will leverage - CPU, a positive will run the Zero-Shot model on the associated CUDA - device id. -- `model` _str, Optional, defaults to 'facebook/bart-large-mnli'_ - The - Zero-Shot model that will be used to classify the topic. See a - list of all models here: - https://huggingface.co/models?pipeline_tag=zero-shot-classification - llm_callable (Union[str, Callable, None], Optional, defaults to -- `'gpt-3.5-turbo')` - Either the name of the OpenAI model, or a callable - that takes a prompt and returns a response. -- `disable_classifier` _bool, Optional, defaults to False_ - controls whether - to use the Zero-Shot model. At least one of disable_classifier and - disable_llm must be False. -- `disable_llm` _bool, Optional, defaults to False_ - controls whether to use - the LLM fallback. At least one of disable_classifier and - disable_llm must be False. -- `model_threshold` _float, Optional, defaults to 0.5_ - The threshold used to - determine whether to accept a topic from the Zero-Shot model. Must be - a number between 0 and 1. - -#### call\_llm(text: str, topics: List[str]) - -```python -@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(5)) -def call_llm(text: str, topics: List[str]) -> str -``` - -Call the LLM with the given prompt. - -Expects a function that takes a string and returns a string. - -**Arguments**: - -- `text` _str_ - The input text to classify using the LLM. -- `topics` _List[str]_ - The list of candidate topics. - -**Returns**: - -- `response` _str_ - String representing the LLM response. - -#### set\_callable(llm\_callable: Union[str, Callable, None]) - -```python -def set_callable(llm_callable: Union[str, Callable, None]) -> None -``` - -Set the LLM callable. - -**Arguments**: - -- `llm_callable` - Either the name of the OpenAI model, or a callable that takes - a prompt and returns a response. - -## UpperCase - -Validates that a value is upper case. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `upper-case` | -| Supported data types | `string` | -| Programmatic fix | Convert to upper case. | - -## ProvenanceV0 - -Validates that LLM-generated text matches some source text based on -distance in embedding space. - -**Key Properties** - -| Property | Description | -| ----------------------------- | ----------------------------------- | -| Name for `format` attribute | `provenance-v0` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `threshold` - The minimum cosine similarity between the generated text and - the source text. Defaults to 0.8. -- `validation_method` - Whether to validate at the sentence level or over the full text. Must be one of `sentence` or `full`. Defaults to `sentence` - - Other parameters: Metadata -- `query_function` _Callable, optional_ - A callable that takes a string and returns a list of (chunk, score) tuples. -- `sources` _List[str], optional_ - The source text. -- `embed_function` _Callable, optional_ - A callable that creates embeddings for the sources. Must accept a list of strings and return an np.array of floats. - - In order to use this validator, you must provide either a `query_function` or - `sources` with an `embed_function` in the metadata. - - If providing query_function, it should take a string as input and return a list of - (chunk, score) tuples. The chunk is a string and the score is a float representing - the cosine distance between the chunk and the input string. The list should be - sorted in ascending order by score. - -- `Note` - The score should represent distance in embedding space, not similarity. I.e., - lower is better and the score should be 0 if the chunk is identical to the input - string. - - -**Example**: - - ```py - def query_function(text: str, k: int) -> List[Tuple[str, float]]: - return [("This is a chunk", 0.9), ("This is another chunk", 0.8)] - - guard = Guard.from_rail(...) - guard( - openai.ChatCompletion.create(...), - prompt_params={...}, - temperature=0.0, - metadata={"query_function": query_function}, - ) - ``` - - - If providing sources, it should be a list of strings. The embed_function should - take a string or a list of strings as input and return a np array of floats. - The vector should be normalized to unit length. - - -**Example**: - - ```py - def embed_function(text: Union[str, List[str]]) -> np.ndarray: - return np.array([[0.1, 0.2, 0.3]]) - - guard = Guard.from_rail(...) - guard( - openai.ChatCompletion.create(...), - prompt_params={...}, - temperature=0.0, - metadata={ - "sources": ["This is a source text"], - "embed_function": embed_function - }, - ) - ``` - -## ProvenanceV1 - -Validates that the LLM-generated text is supported by the provided -contexts. - -This validator uses an LLM callable to evaluate the generated text against the -provided contexts (LLM-ception). - -In order to use this validator, you must provide either: -1. a 'query_function' in the metadata. That function should take a string as input -(the LLM-generated text) and return a list of relevant -chunks. The list should be sorted in ascending order by the distance between the -chunk and the LLM-generated text. - -Example using str callable: - - -Example using a custom llm callable: - - -OR - -2. `sources` with an `embed_function` in the metadata. The embed_function should -take a string or a list of strings as input and return a np array of floats. -The vector should be normalized to unit length. - -``` py -def query_function(text: str, k: int) -> List[str]: - return ["This is a chunk", "This is another chunk"] - -guard = Guard.from_string(validators=[ - ProvenanceV1(llm_callable="gpt-3.5-turbo", ...) -]) -guard.parse( - llm_output=..., - metadata={"query_function": query_function} -) -``` -``` py -def query_function(text: str, k: int) -> List[str]: - return ["This is a chunk", "This is another chunk"] - -guard = Guard.from_string(validators=[ - ProvenanceV1(llm_callable=your_custom_callable, ...) - ] -) -guard.parse( - llm_output=..., - metadata={"query_function": query_function} -) -``` - -**Example**: - - -```py -def embed_function(text: Union[str, List[str]]) -> np.ndarray: - return np.array([[0.1, 0.2, 0.3]]) - -guard = Guard.from_rail(...) -guard( - openai.ChatCompletion.create(...), - prompt_params={...}, - temperature=0.0, - metadata={ - "sources": ["This is a source text"], - "embed_function": embed_function - }, -) -``` - -#### \_\_init\_\_(validation\_method: str = "sentence", llm\_callable: Union[str, Callable] = "gpt-3.5-turbo", top\_k: int = 3, max\_tokens: int = 2, on\_fail: Optional[Callable] = None, \*\*kwargs) - -```python -def __init__(validation_method: str = "sentence", - llm_callable: Union[str, Callable] = "gpt-3.5-turbo", - top_k: int = 3, - max_tokens: int = 2, - on_fail: Optional[Callable] = None, - **kwargs) -``` - -args: -validation_method (str): Whether to validate at the sentence level or over -the full text. One of `sentence` or `full`. Defaults to `sentence` -llm_callable (Union[str, Callable]): Either the name of the OpenAI model, -or a callable that takes a prompt and returns a response. -top_k (int): The number of chunks to return from the query function. -Defaults to 3. -max_tokens (int): The maximum number of tokens to send to the LLM. -Defaults to 2. - -Other args: Metadata -query_function (Callable): A callable that takes a string and returns a -list of chunks. -sources (List[str], optional): The source text. -embed_function (Callable, optional): A callable that creates embeddings for -the sources. Must accept a list of strings and returns float np.array. - -#### set\_callable(llm\_callable: Union[str, Callable]) - -```python -def set_callable(llm_callable: Union[str, Callable]) -> None -``` - -Set the LLM callable. - -**Arguments**: - -- `llm_callable` - Either the name of the OpenAI model, or a callable that takes - a prompt and returns a response. - -#### call\_llm(prompt: str) - -```python -@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) -def call_llm(prompt: str) -> str -``` - -Call the LLM with the given prompt. - -Expects a function that takes a string and returns a string. - -**Arguments**: - -- `prompt` _str_ - The prompt to send to the LLM. - - -**Returns**: - -- `response` _str_ - String representing the LLM response. - -#### evaluate\_with\_llm(text: str, query\_function: Callable) - -```python -def evaluate_with_llm(text: str, query_function: Callable) -> bool -``` - -Validate that the LLM-generated text is supported by the provided -contexts. - -**Arguments**: - -- `value` _Any_ - The LLM-generated text. -- `query_function` _Callable_ - The query function. - - -**Returns**: - -- `self_eval` - The self-evaluation boolean - -## RegexMatch - -Validates that a value matches a regular expression. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `regex_match` | -| Supported data types | `string` | -| Programmatic fix | Generate a string that matches the regular expression | - -**Arguments**: - -- `regex` - Str regex pattern -- `match_type` - Str in {"search", "fullmatch"} for a regex search or full-match option - -## ExcludeSqlPredicates - -Validates that the SQL query does not contain certain predicates. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `exclude-sql-predicates` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `predicates` - The list of predicates to avoid. - -## SimilarToDocument - -Validates that a value is similar to the document. - -This validator checks if the value is similar to the document by checking -the cosine similarity between the value and the document, using an -embedding. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `similar-to-document` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `document` - The document to use for the similarity check. -- `threshold` - The minimum cosine similarity to be considered similar. Defaults to 0.7. -- `model` - The embedding model to use. Defaults to text-embedding-ada-002. - -#### cosine\_similarity(a: "np.ndarray", b: "np.ndarray") - -```python -@staticmethod -def cosine_similarity(a: "np.ndarray", b: "np.ndarray") -> float -``` - -Calculate the cosine similarity between two vectors. - -**Arguments**: - -- `a` - The first vector. -- `b` - The second vector. - - -**Returns**: - -- `float` - The cosine similarity between the two vectors. - -## IsHighQualityTranslation - -Validates that the translation is of high quality. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `is-high-quality-translation` | -| Supported data types | `string` | -| Programmatic fix | None | - -Other parameters: Metadata -translation_source (str): The source of the translation. - -This validator uses one of the reference-free models from Unbabel/COMET -to check the quality of the translation. Specifically, it uses the -`Unbabel/wmt22-cometkiwi-da` model. - -Unbabel/COMET details: https://github.com/Unbabel/COMET -Model details: https://huggingface.co/Unbabel/wmt22-cometkiwi-da - -Pre-requisites: -- Install the `unbabel-comet` from source: -`pip install git+https://github.com/Unbabel/COMET` -- Please accept the model license from: -https://huggingface.co/Unbabel/wmt22-cometkiwi-da -- Login into Huggingface Hub using: -huggingface-cli login --token $HUGGINGFACE_TOKEN - -## EndpointIsReachable - -Validates that a value is a reachable URL. - -**Key Properties** - -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `is-reachable` | -| Supported data types | `string`, | -| Programmatic fix | None | - -## ToxicLanguage - -Validates that the generated text is toxic. - -**Key Properties** -| Property | Description | -| ----------------------------- | --------------------------------- | -| Name for `format` attribute | `toxic-language` | -| Supported data types | `string` | -| Programmatic fix | None | - -**Arguments**: - -- `threshold` - The confidence threshold (model inference) for toxicity. - Defaults to 0.5. -- `validation_method` - Whether to validate at the sentence level or - over the full text. Must be one of `sentence` or `full`. - Defaults to `sentence` - - This validator uses the pre-trained multi-label model from HuggingFace - - `unitary/unbiased-toxic-roberta` to check whether the generated text is toxic. - If the model predicts any label of: `toxicity`, `severe_toxicity`, - `obscene`, `threat`, `insult`, `identity_attack`, or `sexual_explicit` with - confidence higher than the specified threshold, the validator fails and returns - the generated text with the toxic sentences / entire text removed. Else the - validator returns the generated text as it is. - - If validation_method is `sentence`, the validator will remove the sentences - that are predicted to be toxic and return the remaining sentences. If - validation_method is `full`, the validator will remove the entire text if - the prediction is deemed toxic and return an empty string. - - In our experiments, a threshold of 0.5 worked best, hence set as default here. - However, you can try different values of threshold to see what works best for - your use case. - Link for experiments: https://wandb.ai/ml-guardrails/toxic-language-experiments - -#### get\_toxicity(value: str) - -```python -def get_toxicity(value: str) -> List[str] -``` - -Check whether the generated text is toxic. - -Returns the labels predicted by the model with -confidence higher than the threshold. - -**Arguments**: - -- `value` _str_ - The generated text. - - -**Returns**: - -- `pred_labels` _bool_ - Labels predicted by the model - with confidence higher than the threshold. - -#### validate\_each\_sentence(value: str, metadata: Dict[str, Any]) - -```python -def validate_each_sentence(value: str, - metadata: Dict[str, Any]) -> ValidationResult -``` - -Validate that each sentence in the generated text is toxic. - -#### validate\_full\_text(value: str, metadata: Dict[str, Any]) - -```python -def validate_full_text(value: str, metadata: Dict[str, - Any]) -> ValidationResult -``` - -Validate that the entire generated text is toxic. - -## OneLine - -Validates that a value is a single line, based on whether or not the -output has a newline character (\n). - -**Key Properties** - -| Property | Description | -| ----------------------------- | -------------------------------------- | -| Name for `format` attribute | `one-line` | -| Supported data types | `string` | -| Programmatic fix | Keep the first line, delete other text | - diff --git a/docs/hub/concepts/validators.md b/docs/hub/concepts/validators.md index 578b8b9f5..1530ec1a7 100644 --- a/docs/hub/concepts/validators.md +++ b/docs/hub/concepts/validators.md @@ -18,7 +18,7 @@ As an example, the `ExtractedSummarySentencesMatch` validator accepts a `filepat guard = Guard.from_rail("my_railspec.rail") outcome = guard( - llm_api=openai.ChatCompletion.create, + llm_api=openai.chat.completions.create, model="gpt-3.5-turbo", num_reasks=3, metadata={ diff --git a/docs/hub/how_to_guides/input_validation.md b/docs/hub/how_to_guides/input_validation.md index 2e04bab4d..b026e4be8 100644 --- a/docs/hub/how_to_guides/input_validation.md +++ b/docs/hub/how_to_guides/input_validation.md @@ -2,28 +2,35 @@ Validators that are tagged as input validators can be used to validate the input prompt before it is sent to the model. This can be useful for ensuring that the input prompt meets certain criteria, such as being on-topic, not containing PII, etc. -In order to use an input validator, first make sure that the validator is installed. You can install the validator using the `guardrails hub install` command. For example, to install the `two-words` validator, you can run: +In order to use an input validator, first make sure that the validator is installed. You can install the validator using the `guardrails hub install` command. For example, to install the `DetectPII` validator, you can run: ```bash guardrails hub install hub://guardrails/detect_pii ``` -Then, add the input validator to the `Guard` object using the `with_prompt_validation` method. For example, to use the `detect_pii` validator with OpenAI's GPT-3, you can run: +Then, add the input validator to the `Guard` object via the `use` method. For example, to use the `DetectPII` validator with OpenAI's GPT-3, you can run: ```python -from guardrails import Guard, ValidatorError +import openai +from guardrails import Guard +from guardrails.errors import ValidationError from guardrails.hub import DetectPII +from guardrails.types import OnFailAction guard = Guard() -guard.with_prompt_validation([DetectPII( - pii_entities=["EMAIL_ADDRESS", "PHONE_NUMBER"]) -]) +guard.use( + DetectPII( + pii_entities=["EMAIL_ADDRESS", "PHONE_NUMBER"], + on_fail=OnFailAction.EXCEPTION + ), + on="prompt" +) try: guard( openai.chat.completions.create, prompt="My email address is not_a_real_email@guardrailsai.com", ) -except ValidatorError as e: +except ValidationError as e: print(e) ``` diff --git a/docs/integrations/azure_openai.ipynb b/docs/integrations/azure_openai.ipynb index cb3cd20d2..9040ef06e 100644 --- a/docs/integrations/azure_openai.ipynb +++ b/docs/integrations/azure_openai.ipynb @@ -119,7 +119,7 @@ ], "source": [ "raw_llm_output, validated_output, *rest = guard(\n", - " openai.ChatCompletion.create,\n", + " openai.chat.completions.create,\n", " engine=os.environ.get(\"AZURE_OPENAI_API_ENGINE\"),\n", " max_tokens=1024,\n", " temperature=0.3\n", diff --git a/docs/integrations/langchain.ipynb b/docs/integrations/langchain.ipynb index b82d75042..37090e04d 100644 --- a/docs/integrations/langchain.ipynb +++ b/docs/integrations/langchain.ipynb @@ -57,7 +57,7 @@ "\n", "${doctors_notes}\n", "\n", - "${gr.complete_json_suffix_v2}\n", + "${gr.complete_xml_suffix_v2}\n", "\n", "\n", "\"\"\"" diff --git a/docs/integrations/openai_functions.ipynb b/docs/integrations/openai_functions.ipynb index 029dee8a9..ba4e2dea1 100644 --- a/docs/integrations/openai_functions.ipynb +++ b/docs/integrations/openai_functions.ipynb @@ -78,7 +78,7 @@ "guard = gd.Guard.from_pydantic(Director, prompt=\"Generate data about a movie director.\")\n", "\n", "raw_llm_output, validated_output, *rest = guard(\n", - " openai.ChatCompletion.create,\n", + " openai.chat.completions.create,\n", " model=\"gpt-4-0613\",\n", " max_tokens=1024,\n", " temperature=0.0,\n", diff --git a/docs/llm_api_wrappers.md b/docs/llm_api_wrappers.md index 80fd31c1d..c62f6e090 100644 --- a/docs/llm_api_wrappers.md +++ b/docs/llm_api_wrappers.md @@ -19,9 +19,9 @@ guard = gd.Guard.from_rail(...) # Wrap openai API call raw_llm_output, guardrail_output, *rest = guard( - openai.Completion.create, + openai.completions.create, prompt_params={"prompt_param_1": "value_1", "prompt_param_2": "value_2", ..}, - engine="text-davinci-003", + model="gpt-3.5-turbo-instruct", max_tokens=100, temperature=0.0, ) @@ -38,7 +38,7 @@ guard = gd.Guard.from_rail(...) # Wrap openai API call raw_llm_output, guardrail_output, *rest = guard( - openai.ChatCompletion.create, + openai.chat.completions.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", diff --git a/docs/migration_guides/0-5-migration.md b/docs/migration_guides/0-5-migration.md new file mode 100644 index 000000000..ee8f9d49b --- /dev/null +++ b/docs/migration_guides/0-5-migration.md @@ -0,0 +1,281 @@ +# Migrating to 0.5.0 + + +## New Features + +### Run Guardrails as a local server + +Guardrails 0.5.0 introduces the `start` command to the guardrails cli. This allows you to run the Guardrails validation engine as a local python server. + +Benefits of using Guardrails this way include: + +- Less strain on your main process/thread +- The Guardrails server utilizes Gunicorn to take advantage of multiple threads + - Supported on Linux and MacOS by default, supported on Windows when using WSL +- Declare your Guards in a separate config and reference them by name in your app to keep your code slim and clean + +Example: + +1. Install +```sh +pip install "guardarils-ai>=0.5.0" +guardrails hub install hub://guardrails/regex_match +``` + +2. Create a `config.py` +```py +from guardrails import Guard +from guardrails.hub import RegexMatch + + +Guard( + name='name-case', + description='Checks that a string is in Name Case format.' +).use( + RegexMatch(regex="^[A-Z][a-z\\s]*$") +) +``` + +3. Start the Guardrails server +```sh +guardrails start --config=config.py +``` + +4. Use the Guard in your application +```py +from rich import print +from guardrails import Guard + +name_case = Guard(name='name-case') + +result = name_case.validate("Zayd") + +print(result) +``` + + +### Generate structured data with smaller models: + +As part of Guardrails 0.5.0, we're launching constrained decoding support for HuggingFace models. This allow you to generate structured data that matches your schema with confidence. + +Example: +```py +from guardrails import Guard +from pydantic import BaseModel + +class Dog(BaseModel): + name: str + color: str + weight_kg: float + +class NewFriends(BaseModel): + dogs: list[Dog] + +guard = Guard.from_pydantic(NewFriends, output_formatter="jsonformer") + +# JSONFormer is only compatible with HF Pipelines and HF Models: +from transformers import pipeline +tiny_llama_pipeline = pipeline("text-generation", "TinyLlama/TinyLlama-1.1B-Chat-v1.0") + +# Inference is straightforward: +response = guard(tiny_llama_pipeline, prompt="Please enjoy this list of good dogs:") + +# `out` is a dict. Format it as JSON for readability: +import json +print(json.dumps(response.validated_output, indent=2)) +``` + + +## Improvements + +### LiteLLM is now easier to use within Guardrails + +When calling models through LiteLLM, specifying the `llm_api` argument is now optional. Instead, just pass the model name. + +Example: + +```py +from rich import print +from guardrails import Guard +from guardrails.hub import RegexMatch + +guard = Guard().use(RegexMatch("95", match_type="search")) + +response = guard( + model="gpt-4o", + instructions="You are a helpful assistant.", + prompt="How many moons does jupiter have?", +) + +print(response) +``` + +### New public interface for generating JSON schema-based function calling tools + +Guardrails has supported function calling for OpenAI Chat models for a while and previously would auto-insert a function to specify the schema when a Guard was created via a Pydantic model. + +In Guardrails 0.5.0, you can use this same pattern regardless of how the Guard was initialized. We also made the process more transparent by allowing you to generate the tool first and decide when to pass it as a keyword argument. For models that support openai tool/function calling (`gpt-4o`, `gpt-4-turbo`, or `gpt-3.5-turbo`), you can extend your existing `tools` with `Guard.add_json_function_calling_tool()` + +Example: +```py +from guardrails import Guard +from guardrails.hub import RegexMatch +from pydantic import BaseModel, Field +from typing import List + +NAME_REGEX = "^[A-Z][a-z]+\s[A-Z][a-z]+$" + +class Delivery(BaseModel): + custome_name: str=Field(validators=[RegexMatch(regex=NAME_REGEX)], description="customer name") + pickup_time: str=Field(description="date and time of pickup") + pickup_location: str=Field(description="address of pickup") + dropoff_time: str=Field(description="date and time of dropoff") + dropoff_location: str=Field(description="address of dropoff") + price: str = Field(description="price of delivery with currency symbol included") + +class Schedule(BaseModel): + deliveries: List[Delivery] + +guard = Guard.from_pydantic(Schedule) +chat_history=""" +nelson and murdock: i need a pickup 797 9th Avenue, manila envelope, June 3 10:00am with dropoff 10:30am Courthouse, 61 Center Street C/O frank james +operator: quote - $23.00 +neslon and murdock: perfect, we accept the quote +operator: 797 9th ave, 10:00am pickup comfirmed +abc flowers: i need a pickup of a flowers from abc flowers at 21 3rd street at 11:00am on june 2 with a dropoff at 75th Ave at 5:30pm same day +operator: 21 3rd street flowers quote - $14.50 +abc flowers: accepted +polk and wardell: i need a pickup of a bagels from Bakers Co at 331 5th street at 11:00am on june 3 with a dropoff at 75th Ave at 5:30pm same day +operator: 331 5th street bagels quote - $34.50 +polk and wardell: accepted +""" + +prompt = """ +From the chat exchanges below extract a schedule of deliveries. +Chats: +${chat_history} +""" + +tools = [] # an open ai compatible list of tools + +response = guard( + openai.chat.completions.create, + model="gpt-4o", + instructions="You are a helpful assistant.", + prompt=prompt, + prompt_params={"chat_history": chat_history}, + tools=guard.add_json_function_calling_tool(tools), + tool_choice="required", +) +``` + +### `Guard.use()` now works for all Guards + +Previously, constructing a Guard via the `use` method was only supported for unstructured response schemas. It now supports specifying validators for any Guard regardless of the initialization method (`Guard()`, `Guard.from_rail()`, `Guard.from_pydantic()`, etc.). `Guard.use()` is also the new method of applying input validations to a Guard. + +Example of applying input validation to the Prompt: +```py +import openai +from guardrails import Guard +from guardrails.errors import ValidationError +from guardrails.hub import DetectPII +from guardrails.types import OnFailAction + +guard = Guard() +guard.use( + DetectPII( + pii_entities=["EMAIL_ADDRESS", "PHONE_NUMBER"], + on_fail=OnFailAction.EXCEPTION + ), + on="prompt" +) + +try: + guard( + openai.chat.completions.create, + prompt="My email address is not_a_real_email@guardrailsai.com", + ) +except ValidationError as e: + print(e) +``` + +To utilize `Guard.use()` on a Guard with structured output, you can specify a JSON Path to identify which property the Validator(s) should be assigned to. + +Example: +```py +import json +from pydantic import BaseModel, Field +from guardrails import Guard, OnFailAction +from guardrails.errors import ValidationError +from guardrails.hub import RegexMatch, ValidRange + +class Person(BaseModel): + name: str + # Existing way of assigning validators; still valid + age: int = Field(validators=[ValidRange(0, 100, on_fail=OnFailAction.EXCEPTION)]) + is_employed: bool + +guard = Guard.from_pydantic(Person) + +# Use a regex to make sure the name is Title Case +guard.use( + RegexMatch("^(?:[A-Z][^\\s]*\\s?)+$", on_fail=OnFailAction.EXCEPTION), + on="$.name" +) + +try: + guard.validate(json.dumps({ + "name": "john doe", + "age": 30, + "is_employed": True + })) +except ValidationError as e: + print(e) +``` + +## Backwards-incompatible changes + +### Args vs Kwargs +In previous versions, most of the Guardrails interfaces utilized positional arguments for most parameters. This could be tedious when specifying optional arguments. + +In 0.5.0, for our public interfaces, only required arguments are positional; all optional arguments are keyword only. + +If you previously called you Guard like this: +```py +guard( + openai.chat.completions.create,, + { "topic": "recursion" }, # prompt parameters + 2, # number of reasks + "Write a short statement about ${topic}", # prompt +) +``` + +You will now call it like this: +```py +guard( + openai.chat.completions.create, + prompt_params={ "topic": "recursion" }, + num_reasks=2, + prompt="Write a short statement about ${topic}", +) +``` + +### Validators have moved +We've moved validators to the [Guardrails Hub](https://hub.guardrailsai.com), reducing the core package size for faster installations and smoother workflows. + +Targeted validation: Install only the validators you need, streamlining your project and dependencies. + +New naming: Some validator names changed for clarity and consistency. Head to the hub to find the updated names and grab your validators! + +### AsyncGuard's for Async LLMs +In v0.4.4, we introduced a new `AsyncGuard` for use with asynchronous LLM's. As of 0.5.0, support for async LLM's was removed from the `Guard` class and is now only supported in the `AsyncGuard` class. This should provide better type hinting while developing as well as make the interface simpler and easier to use. + +### Prompt Primitives have moved +In v0.4.5, we introduced `xml` prompt primitives to replace the previous `json` constants. In 0.5.0, the `json` prompt primitives have a different meaning and will likely continue to evolve. If you wish to keep the same constructed prompts as before, you must utilize the new `xml` prompt primitives. + +### Removal of support for older dependency versions +As of 0.5.0, we no longer directly support to following versions of dependencies: + +- Python 3.8 +- Pydantic 1.x +- OpenAI 0.x \ No newline at end of file diff --git a/docs/the_guard.md b/docs/the_guard.md index 9c1c51c95..6837adfaa 100644 --- a/docs/the_guard.md +++ b/docs/the_guard.md @@ -19,8 +19,8 @@ from guardrails import Guard guard = Guard.from_rail(...) raw_output, validated_output, *rest = guard( - openai.Completion.create, - engine="text-davinci-003", + openai.completions.create, + model="gpt-3.5-turbo-instruct", max_tokens=1024, temperature=0.3 ) @@ -43,8 +43,8 @@ output = call_my_llm() validated_output = guard.parse( llm_output=output, - llm_api=openai.Completion.create, - engine="text-davinci-003", + llm_api=openai.completions.create, + model="gpt-3.5-turbo-instruct", max_tokens=1024, temperature=0.3, num_reasks=2 diff --git a/guardrails/__init__.py b/guardrails/__init__.py index b1d081fca..dd3bf9b1c 100644 --- a/guardrails/__init__.py +++ b/guardrails/__init__.py @@ -5,15 +5,14 @@ from guardrails.llm_providers import PromptCallableBase from guardrails.logging_utils import configure_logging from guardrails.prompt import Instructions, Prompt -from guardrails.rail import Rail from guardrails.utils import constants, docs_utils -from guardrails.validator_base import OnFailAction, Validator, register_validator +from guardrails.types.on_fail import OnFailAction +from guardrails.validator_base import Validator, register_validator __all__ = [ "Guard", "AsyncGuard", "PromptCallableBase", - "Rail", "Validator", "OnFailAction", "register_validator", diff --git a/guardrails/actions/__init__.py b/guardrails/actions/__init__.py new file mode 100644 index 000000000..c8aa2b109 --- /dev/null +++ b/guardrails/actions/__init__.py @@ -0,0 +1,16 @@ +from guardrails.actions.filter import Filter, apply_filters + +from guardrails.actions.reask import ReAsk, FieldReAsk, SkeletonReAsk, NonParseableReAsk + +from guardrails.actions.refrain import Refrain, apply_refrain + +__all__ = [ + "Filter", + "apply_filters", + "ReAsk", + "FieldReAsk", + "SkeletonReAsk", + "NonParseableReAsk", + "Refrain", + "apply_refrain", +] diff --git a/guardrails/actions/filter.py b/guardrails/actions/filter.py new file mode 100644 index 000000000..8d25dd9ee --- /dev/null +++ b/guardrails/actions/filter.py @@ -0,0 +1,37 @@ +from typing import Any, Dict, List + + +class Filter: + pass + + +def apply_filters(value: Any) -> Any: + if isinstance(value, Filter): + pass + elif isinstance(value, List): + # Cleaner syntax but requires two iterations + # filtered_list = list(filter(None, map(apply_filters, value))) + filtered_list = [] + for item in value: + filtered_item = apply_filters(item) + if filtered_item is not None: + filtered_list.append(filtered_item) + + return filtered_list + elif isinstance(value, Dict): + # Cleaner syntax but requires two iterations + # filtered_dict = { + # k: apply_filters(v) + # for k, v in value.items() + # if apply_filters(v) + # } + filtered_dict = {} + for k, v in value.items(): + # Should we omit the key or just the value? + filtered_value = apply_filters(v) + if filtered_value is not None: + filtered_dict[k] = filtered_value + + return filtered_dict + else: + return value diff --git a/guardrails/actions/reask.py b/guardrails/actions/reask.py new file mode 100644 index 000000000..3ffa8b0ec --- /dev/null +++ b/guardrails/actions/reask.py @@ -0,0 +1,624 @@ +from copy import deepcopy +import json +from typing import Any, Dict, List, Optional, Sequence, Tuple, Union + +from guardrails_api_client import Reask as IReask +from guardrails.classes.execution.guard_execution_options import GuardExecutionOptions +from guardrails.classes.output_type import OutputTypes +from guardrails.classes.validation.validation_result import FailResult +from guardrails.prompt.instructions import Instructions +from guardrails.prompt.prompt import Prompt +from guardrails.schema.generator import generate_example +from guardrails.schema.rail_schema import json_schema_to_rail_output +from guardrails.types.validator import ValidatorMap +from guardrails.utils.constants import constants +from guardrails.utils.prompt_utils import prompt_content_for_schema, prompt_uses_xml + + +### Classes/Types ### +class ReAsk(IReask): + incorrect_value: Any + fail_results: List[FailResult] + + @classmethod + def from_interface(cls, reask: IReask) -> "ReAsk": + fail_results = [] + if reask.fail_results: + fail_results: List[FailResult] = [ + FailResult.from_interface(fail_result) + for fail_result in reask.fail_results + ] + + if reask.additional_properties.get("path"): + return FieldReAsk( + incorrect_value=reask.incorrect_value, + fail_results=fail_results, + path=reask.additional_properties["path"], + ) + + if len(fail_results) == 1: + error_message = fail_results[0].error_message + if error_message == "Output is not parseable as JSON": + return NonParseableReAsk( + incorrect_value=reask.incorrect_value, + fail_results=fail_results, + ) + elif "JSON does not match schema" in error_message: + return SkeletonReAsk( + incorrect_value=reask.incorrect_value, + fail_results=fail_results, + ) + + return cls(incorrect_value=reask.incorrect_value, fail_results=fail_results) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> Optional["ReAsk"]: + i_reask = super().from_dict(obj) + if not i_reask: + return None + return cls.from_interface(i_reask) + + +class FieldReAsk(ReAsk): + # FIXME: This shouldn't be optional + # We should be able to assign it on init now + path: Optional[List[Any]] = None + + +class SkeletonReAsk(ReAsk): + pass + + +class NonParseableReAsk(ReAsk): + pass + + +### Internal Helper Methods ### +def get_reask_subschema( + json_schema: Dict[str, Any], + reasks: Optional[List[FieldReAsk]] = None, +) -> Dict[str, Any]: + """Prune schema of any subschemas that are not in `reasks`. + + Return the schema with only the subschemas that are being `reask`ed for and + their parents. If `reasks` is None, return the entire schema. If an + subschema is removed, remove all ancestors that have no children. + + Args: + root: A JSON Schema + reasks: The fields that are to be reasked. + + Returns: + A JSON Schema. + """ + root = deepcopy(json_schema) + + if reasks is None: + return root + + # Find all elements that are to be retained + # NOTE: At this point, in the case of discriminated unions, + # the LLM has already decided which subschema of the union to use. + # This means that we can flatten complex schema compositions, e.g. anyOf's, + # and just build a subschema that represents the resolved schema + # of the LLM response. + # schema_paths_to_retain = [] + # for reask in reasks: + # path = reask.path + # if path is None: + # raise RuntimeError("FieldReAsk path is None") + # schema_path = "$" + # for part in path: + # if isinstance(part, int): + # schema_path += ".items" + # else: + # schema_path += f".properties.{path}" + # schema_paths_to_retain.append(schema_path) + + # # Remove all elements that are not to be retained + # def _prune_schema(schema: Dict[str, Any]) -> None: + # if schema.get("type") == SimpleTypes.ARRAY: + # if schema.children.item not in retain: + # del schema._children["item"] + # else: + # _prune_schema(schema.children.item) + # else: # if isinstance(schema, ObjectType): + # for child_name, child in vars(schema.children).items(): + # if child not in retain: + # del schema._children[child_name] + # else: + # _prune_schema(child) + + # _prune_schema(root) + + # FIXME: PUNT + return root + + +def prune_obj_for_reasking(obj: Any) -> Union[None, Dict, List, ReAsk]: + """After validation, we get a nested dictionary where some keys may be + ReAsk objects. + + This function prunes the validated form of any object that is not a ReAsk object. + It also keeps all of the ancestors of the ReAsk objects. + + Args: + obj: The validated object. + + Returns: + The pruned validated object. + """ + + if isinstance(obj, ReAsk): + return obj + elif isinstance(obj, list): + pruned_list = [] + for item in obj: + pruned_output = prune_obj_for_reasking(item) + if pruned_output is not None: + pruned_list.append(pruned_output) + if len(pruned_list): + return pruned_list + return None + elif isinstance(obj, dict): + pruned_json = {} + for key, value in obj.items(): + if isinstance(value, FieldReAsk): + pruned_json[key] = value + elif isinstance(value, dict): + pruned_output = prune_obj_for_reasking(value) + if pruned_output is not None: + pruned_json[key] = pruned_output + elif isinstance(value, list): + pruned_list = [] + for item in value: + pruned_output = prune_obj_for_reasking(item) + if pruned_output is not None: + pruned_list.append(pruned_output) + if len(pruned_list): + pruned_json[key] = pruned_list + + if len(pruned_json): + return pruned_json + + return None + + +def update_response_by_path(output: dict, path: List[Any], value: Any) -> None: + """Update the output by path. + + Args: + output: The output. + path: The path to the element to be updated. + value: The value to be updated. + """ + for key in path[:-1]: + output = output[key] + output[path[-1]] = value + + +### Guard Execution Methods ### +def introspect( + data: Optional[Union[ReAsk, str, Dict, List]], +) -> Tuple[Sequence[ReAsk], Optional[Union[str, Dict, List]]]: + if isinstance(data, FieldReAsk): + return [data], None + elif isinstance(data, SkeletonReAsk): + return [data], None + elif isinstance(data, NonParseableReAsk): + return [data], None + return gather_reasks(data) + + +def get_reask_setup_for_string( + output_type: OutputTypes, + output_schema: Dict[str, Any], + validation_map: ValidatorMap, + reasks: Sequence[ReAsk], + *, + validation_response: Optional[Union[str, List, Dict, ReAsk]] = None, + prompt_params: Optional[Dict[str, Any]] = None, + exec_options: Optional[GuardExecutionOptions] = None, +) -> Tuple[Dict[str, Any], Prompt, Instructions]: + prompt_params = prompt_params or {} + exec_options = exec_options or GuardExecutionOptions() + + schema_prompt_content = prompt_content_for_schema( + output_type, output_schema, validation_map + ) + xml_output_schema = json_schema_to_rail_output( + json_schema=output_schema, validator_map=validation_map + ) + + reask_prompt_template = None + if exec_options.reask_prompt: + reask_prompt_template = Prompt(exec_options.reask_prompt) + else: + reask_prompt_template = Prompt( + constants["high_level_string_reask_prompt"] + + constants["complete_string_suffix"] + ) + + error_messages = "\n".join( + [ + f"- {fail_result.error_message}" + for reask in reasks + for fail_result in reask.fail_results + ] + ) + + prompt = reask_prompt_template.format( + # FIXME: How do we properly type this? + # Solution will have to come from Runner all the way down to here + previous_response=validation_response.incorrect_value, # type: ignore + error_messages=error_messages, + output_schema=schema_prompt_content, + xml_output_schema=xml_output_schema, + **prompt_params, + ) + + instructions = None + if exec_options.reask_instructions: + instructions = Instructions(exec_options.reask_instructions) + if instructions is None: + instructions = Instructions("You are a helpful assistant.") + instructions = instructions.format( + output_schema=schema_prompt_content, + xml_output_schema=xml_output_schema, + **prompt_params, + ) + + return output_schema, prompt, instructions + + +def get_original_prompt(exec_options: Optional[GuardExecutionOptions] = None) -> str: + exec_options = exec_options or GuardExecutionOptions() + original_msg_history = exec_options.msg_history or [] + msg_history_prompt = next( + ( + h.get("content") + for h in original_msg_history + if isinstance(h, dict) and h.get("role") == "user" + ), + "", + ) + original_prompt = exec_options.prompt or msg_history_prompt or "" + return original_prompt + + +def get_reask_setup_for_json( + output_type: OutputTypes, + output_schema: Dict[str, Any], + validation_map: ValidatorMap, + reasks: Sequence[ReAsk], + *, + parsing_response: Optional[Union[str, List, Dict, ReAsk]] = None, + validation_response: Optional[Union[str, List, Dict, ReAsk]] = None, + use_full_schema: Optional[bool] = False, + prompt_params: Optional[Dict[str, Any]] = None, + exec_options: Optional[GuardExecutionOptions] = None, +) -> Tuple[Dict[str, Any], Prompt, Instructions]: + reask_schema = output_schema + is_skeleton_reask = not any(isinstance(reask, FieldReAsk) for reask in reasks) + is_nonparseable_reask = any( + isinstance(reask, NonParseableReAsk) for reask in reasks + ) + error_messages = {} + prompt_params = prompt_params or {} + exec_options = exec_options or GuardExecutionOptions() + original_prompt = get_original_prompt(exec_options) + use_xml = prompt_uses_xml(original_prompt) + + reask_prompt_template = None + if exec_options.reask_prompt: + reask_prompt_template = Prompt(exec_options.reask_prompt) + + if is_nonparseable_reask: + if reask_prompt_template is None: + suffix = ( + constants["xml_suffix_without_examples"] + if use_xml + else constants["json_suffix_without_examples"] + ) + reask_prompt_template = Prompt( + constants["high_level_json_parsing_reask_prompt"] + suffix + ) + np_reask: NonParseableReAsk = next( + r for r in reasks if isinstance(r, NonParseableReAsk) + ) + # Give the LLM what it gave us that couldn't be parsed as JSON + reask_value = np_reask.incorrect_value + elif is_skeleton_reask: + if reask_prompt_template is None: + reask_prompt = constants["high_level_skeleton_reask_prompt"] + + if use_xml: + reask_prompt = ( + reask_prompt + constants["xml_suffix_with_structure_example"] + ) + else: + reask_prompt = ( + reask_prompt + + constants["error_messages"] + + constants["json_suffix_with_structure_example"] + ) + + reask_prompt_template = Prompt(reask_prompt) + + # Validation hasn't happend yet + # and the problem is with the json the LLM gave us. + # Give it this same json and tell it to fix it. + reask_value = validation_response if use_xml else parsing_response + skeleton_reask: SkeletonReAsk = next( + r for r in reasks if isinstance(r, SkeletonReAsk) + ) + error_messages = skeleton_reask.fail_results[0].error_message + else: + if use_full_schema: + # Give the LLM the full JSON that failed validation + reask_value = validation_response if use_xml else parsing_response + # Don't prune the tree if we're reasking with pydantic model + # (and openai function calling) + else: + # Prune out the individual fields that did not fail validation. + # Only reask for field that did fail. + reask_value = prune_obj_for_reasking(validation_response) + + # Generate a subschema that matches the specific fields we're reasking for. + field_reasks = [r for r in reasks if isinstance(r, FieldReAsk)] + reask_schema = get_reask_subschema(output_schema, field_reasks) + + if reask_prompt_template is None: + suffix = ( + constants["xml_suffix_without_examples"] + if use_xml + else constants["json_suffix_without_examples"] + ) + reask_prompt_template = Prompt( + constants["high_level_json_reask_prompt"] + suffix + ) + + error_messages = { + ".".join(str(p) for p in r.path): "; ".join( # type: ignore + f.error_message for f in r.fail_results + ) + for r in reasks + if isinstance(r, FieldReAsk) + } + + stringified_schema = prompt_content_for_schema( + output_type, reask_schema, validation_map + ) + xml_output_schema = json_schema_to_rail_output( + json_schema=output_schema, validator_map=validation_map + ) + + json_example = json.dumps( + generate_example(reask_schema), + indent=2, + ) + + def reask_decoder(obj: ReAsk): + decoded = {} + for k, v in obj.__dict__.items(): + if k in ["path", "additional_properties"]: + continue + if k == "fail_results": + k = "error_messages" + v = [result.error_message for result in v] + decoded[k] = v + return decoded + + prompt = reask_prompt_template.format( + previous_response=json.dumps( + reask_value, indent=2, default=reask_decoder, ensure_ascii=False + ), + output_schema=stringified_schema, + xml_output_schema=xml_output_schema, + json_example=json_example, + error_messages=json.dumps(error_messages), + **prompt_params, + ) + + instructions = None + if exec_options.reask_instructions: + instructions = Instructions(exec_options.reask_instructions) + else: + instructions_const = ( + constants["high_level_xml_instructions"] + if use_xml + else constants["high_level_json_instructions"] + ) + instructions = Instructions(instructions_const) + instructions = instructions.format(**prompt_params) + + return reask_schema, prompt, instructions + + +def get_reask_setup( + output_type: OutputTypes, + output_schema: Dict[str, Any], + validation_map: ValidatorMap, + reasks: Sequence[ReAsk], + *, + parsing_response: Optional[Union[str, List, Dict, ReAsk]] = None, + validation_response: Optional[Union[str, List, Dict, ReAsk]] = None, + use_full_schema: Optional[bool] = False, + prompt_params: Optional[Dict[str, Any]] = None, + exec_options: Optional[GuardExecutionOptions] = None, +) -> Tuple[Dict[str, Any], Prompt, Instructions]: + prompt_params = prompt_params or {} + exec_options = exec_options or GuardExecutionOptions() + + if output_type == OutputTypes.STRING: + return get_reask_setup_for_string( + output_type=output_type, + output_schema=output_schema, + validation_map=validation_map, + reasks=reasks, + validation_response=validation_response, + prompt_params=prompt_params, + exec_options=exec_options, + ) + return get_reask_setup_for_json( + output_type=output_type, + output_schema=output_schema, + validation_map=validation_map, + reasks=reasks, + parsing_response=parsing_response, + validation_response=validation_response, + use_full_schema=use_full_schema, + prompt_params=prompt_params, + exec_options=exec_options, + ) + + +### Post-Processing Methods ### +def gather_reasks( + validated_output: Optional[Union[ReAsk, str, Dict, List]], +) -> Tuple[List[ReAsk], Optional[Union[str, Dict, List]]]: + """Traverse output and gather all ReAsk objects. + + Args: + validated_output (Union[str, Dict, ReAsk], optional): The output of a model. + Each value can be a ReAsk, a list, a dictionary, or a single value. + + Returns: + A list of ReAsk objects found in the output. + """ + if validated_output is None: + return [], None + if isinstance(validated_output, ReAsk): + return [validated_output], None + if isinstance(validated_output, str): + return [], validated_output + + reasks = [] + + def _gather_reasks_in_dict( + original: Dict, valid_output: Dict, path: Optional[List[Union[str, int]]] = None + ) -> None: + if path is None: + path = [] + for field, value in original.items(): + if isinstance(value, FieldReAsk): + value.path = path + [field] + reasks.append(value) + del valid_output[field] + + if isinstance(value, dict): + _gather_reasks_in_dict(value, valid_output[field], path + [field]) + + if isinstance(value, list): + _gather_reasks_in_list(value, valid_output[field], path + [field]) + return + + def _gather_reasks_in_list( + original: List, valid_output: List, path: Optional[List[Union[str, int]]] = None + ) -> None: + if path is None: + path = [] + for idx, item in enumerate(original): + if isinstance(item, FieldReAsk): + item.path = path + [idx] + reasks.append(item) + del valid_output[idx] + elif isinstance(item, dict): + _gather_reasks_in_dict(item, valid_output[idx], path + [idx]) + elif isinstance(item, list): + _gather_reasks_in_list(item, valid_output[idx], path + [idx]) + return + + if isinstance(validated_output, Dict): + valid_output = deepcopy(validated_output) + _gather_reasks_in_dict(validated_output, valid_output) + return reasks, valid_output + elif isinstance(validated_output, List): + valid_output = deepcopy(validated_output) + _gather_reasks_in_list(validated_output, valid_output) + return reasks, valid_output + return reasks, None + + +def sub_reasks_with_fixed_values(value: Any) -> Any: + """Substitute ReAsk objects with their fixed values recursively. + + Args: + value: Either a list, a dictionary, a ReAsk object or a scalar value. + + Returns: + The value with ReAsk objects replaced with their fixed values. + """ + copy = deepcopy(value) + if isinstance(copy, list): + for index, item in enumerate(copy): + copy[index] = sub_reasks_with_fixed_values(item) + elif isinstance(copy, dict): + for dict_key, dict_value in value.items(): + copy[dict_key] = sub_reasks_with_fixed_values(dict_value) + elif isinstance(copy, FieldReAsk): + fix_value = copy.fail_results[0].fix_value + # TODO handle multiple fail results + # Leave the ReAsk in place if there is no fix value + # This allows us to determine the proper status for the call + copy = fix_value if fix_value is not None else copy + + return copy + + +def merge_reask_output(previous_response, reask_response) -> Dict: + """Merge the reask output into the original output. + + Args: + prev_logs: validation output object from the previous iteration. + current_logs: validation output object from the current iteration. + + Returns: + The merged output. + """ + if isinstance(previous_response, ReAsk): + return reask_response + + # FIXME: Uncommenet when field level reask is fixed + # This used to be necessary for field level reask because + # the schema was pruned to only the properties that failed. + # This caused previous keys that were correct to be pruned during schemafication. + # pruned_reask_json = prune_obj_for_reasking(previous_response) + pruned_reask_json = previous_response + + # Reask output and reask json have the same structure, except that values + # of the reask json are ReAsk objects. We want to replace the ReAsk objects + # with the values from the reask output. + merged_json = deepcopy(previous_response) + + def update_reasked_elements(pruned_reask_json, reask_response_dict): + if isinstance(pruned_reask_json, dict): + for key, value in pruned_reask_json.items(): + if isinstance(value, FieldReAsk): + if value.path is None: + raise RuntimeError( + "FieldReAsk object must have a path attribute." + ) + corrected_value = reask_response_dict.get(key) + update_response_by_path(merged_json, value.path, corrected_value) + else: + update_reasked_elements( + pruned_reask_json[key], reask_response_dict[key] + ) + elif isinstance(pruned_reask_json, list): + for i, item in enumerate(pruned_reask_json): + if isinstance(item, FieldReAsk): + if item.path is None: + raise RuntimeError( + "FieldReAsk object must have a path attribute." + ) + corrected_value = reask_response_dict[i] + update_response_by_path(merged_json, item.path, corrected_value) + else: + update_reasked_elements( + pruned_reask_json[i], reask_response_dict[i] + ) + + update_reasked_elements(pruned_reask_json, reask_response) + + return merged_json diff --git a/guardrails/actions/refrain.py b/guardrails/actions/refrain.py new file mode 100644 index 000000000..923b746d3 --- /dev/null +++ b/guardrails/actions/refrain.py @@ -0,0 +1,39 @@ +from typing import Any, Dict, List, Union +from guardrails.classes.output_type import OutputTypes +from guardrails.logger import logger + + +class Refrain: + pass + + +def check_for_refrain(value: Union[List, Dict]) -> bool: + if isinstance(value, Refrain): + return True + elif isinstance(value, list): + for item in value: + if check_for_refrain(item): + return True + elif isinstance(value, dict): + for key, child in value.items(): + if check_for_refrain(child): + return True + + return False + + +# Could be a generic instead of Any +def apply_refrain(value: Any, output_type: OutputTypes) -> Any: + refrain_value = {} + if output_type == OutputTypes.STRING: + refrain_value = "" + elif output_type == OutputTypes.LIST: + refrain_value = [] + + if check_for_refrain(value): + # If the data contains a `Refain` value, we return an empty + # value. + logger.debug("Refrain detected.") + value = refrain_value + + return value diff --git a/guardrails/api_client.py b/guardrails/api_client.py index 55af0fdaf..e72c806b5 100644 --- a/guardrails/api_client.py +++ b/guardrails/api_client.py @@ -1,17 +1,26 @@ import json import os -from typing import Generator, Optional +from typing import Any, Iterable, Optional import requests -from guardrails_api_client import AuthenticatedClient -from guardrails_api_client.api.guard import update_guard, validate -from guardrails_api_client.models import Guard, ValidatePayload, ValidationOutput -from guardrails_api_client.types import UNSET -from httpx import Timeout +from guardrails_api_client.configuration import Configuration +from guardrails_api_client.api_client import ApiClient +from guardrails_api_client.api.guard_api import GuardApi +from guardrails_api_client.api.validate_api import ValidateApi +from guardrails_api_client.models import ( + Guard, + ValidatePayload, + ValidationOutcome as IValidationOutcome, +) + +from guardrails.logger import logger class GuardrailsApiClient: - _client: AuthenticatedClient + _api_client: ApiClient + _guard_api: GuardApi + _validate_api: ValidateApi + timeout: float base_url: str api_key: str @@ -24,15 +33,24 @@ def __init__(self, base_url: Optional[str] = None, api_key: Optional[str] = None self.api_key = ( api_key if api_key is not None else os.environ.get("GUARDRAILS_API_KEY", "") ) - self._client = AuthenticatedClient( - base_url=self.base_url, # type: ignore - follow_redirects=True, # type: ignore - token=self.api_key, - timeout=Timeout(300), # type: ignore + self.timeout = 300 + self._api_client = ApiClient( + configuration=Configuration(api_key=self.api_key, host=self.base_url) ) + self._guard_api = GuardApi(self._api_client) + self._validate_api = ValidateApi(self._api_client) def upsert_guard(self, guard: Guard): - update_guard.sync(guard_name=guard.name, client=self._client, body=guard) + self._guard_api.update_guard( + guard_name=guard.name, body=guard, _request_timeout=self.timeout + ) + + def fetch_guard(self, guard_name: str) -> Optional[Guard]: + try: + return self._guard_api.get_guard(guard_name=guard_name) + except Exception as e: + logger.debug(f"Error fetching guard {guard_name}: {e}") + return None def validate( self, @@ -43,12 +61,11 @@ def validate( _openai_api_key = ( openai_api_key if openai_api_key is not None - else os.environ.get("OPENAI_API_KEY", UNSET) + else os.environ.get("OPENAI_API_KEY") ) - return validate.sync( + return self._validate_api.validate( guard_name=guard.name, - client=self._client, - body=payload, + validate_payload=payload, x_openai_api_key=_openai_api_key, ) @@ -57,11 +74,11 @@ def stream_validate( guard: Guard, payload: ValidatePayload, openai_api_key: Optional[str] = None, - ) -> Generator[ValidationOutput, None, None]: + ) -> Iterable[Any]: _openai_api_key = ( openai_api_key if openai_api_key is not None - else os.environ.get("OPENAI_API_KEY", UNSET) + else os.environ.get("OPENAI_API_KEY") ) url = f"{self.base_url}/guards/{guard.name}/validate" @@ -81,4 +98,7 @@ def stream_validate( ) if line: json_output = json.loads(line) - yield ValidationOutput.from_dict(json_output) + yield IValidationOutcome.from_dict(json_output) + + def get_history(self, guard_name: str, call_id: str): + return self._guard_api.get_guard_history(guard_name, call_id) diff --git a/guardrails/applications/text2sql.py b/guardrails/applications/text2sql.py index cf3a91ca7..b2fe86333 100644 --- a/guardrails/applications/text2sql.py +++ b/guardrails/applications/text2sql.py @@ -140,7 +140,7 @@ def _init_guard( rail_spec_str = Template(rail_spec_str).safe_substitute(**rail_params) guard = Guard.from_rail_string(rail_spec_str) - guard.rail.output_schema.reask_prompt_template = reask_prompt + guard._exec_opts.reask_prompt = reask_prompt return guard diff --git a/guardrails/async_guard.py b/guardrails/async_guard.py index 079a6f34a..fca49a948 100644 --- a/guardrails/async_guard.py +++ b/guardrails/async_guard.py @@ -1,3 +1,4 @@ +from builtins import id as object_id import contextvars import inspect from typing import ( @@ -6,32 +7,43 @@ Awaitable, Callable, Dict, - Iterable, + Generic, List, Optional, + Sequence, Union, cast, ) -from guardrails_api_client.models import ValidatePayload, ValidationOutput +from guardrails_api_client.models import ( + ValidatePayload, + ValidationOutcome as IValidationOutcome, +) from guardrails import Guard from guardrails.classes import OT, ValidationOutcome from guardrails.classes.history import Call from guardrails.classes.history.call_inputs import CallInputs +from guardrails.classes.output_type import OutputTypes +from guardrails.classes.schema.processed_schema import ProcessedSchema from guardrails.llm_providers import get_async_llm_ask, model_is_supported_server_side from guardrails.logger import set_scope from guardrails.run import AsyncRunner, AsyncStreamRunner from guardrails.stores.context import ( + Tracer, get_call_kwarg, set_call_kwargs, set_tracer, set_tracer_context, ) +from guardrails.types.pydantic import ModelOrListOfModels +from guardrails.types.validator import UseManyValidatorSpec, UseValidatorSpec +from guardrails.utils.validator_utils import verify_metadata_requirements +from guardrails.validator_base import Validator -class AsyncGuard(Guard): - """The Guard class. +class AsyncGuard(Guard, Generic[OT]): + """The AsyncGuard class. This class one of the main entry point for using Guardrails. It is initialized from one of the following class methods: @@ -47,46 +59,154 @@ class AsyncGuard(Guard): the LLM and the validated output stream. """ - async def __call__( + @classmethod + def _from_rail_schema( + cls, + schema: ProcessedSchema, + rail: str, + *, + num_reasks: Optional[int] = None, + tracer: Optional[Tracer] = None, + name: Optional[str] = None, + description: Optional[str] = None, + ): + guard = super()._from_rail_schema( + schema, + rail, + num_reasks=num_reasks, + tracer=tracer, + name=name, + description=description, + ) + if schema.output_type == OutputTypes.STRING: + return cast(AsyncGuard[str], guard) + elif schema.output_type == OutputTypes.LIST: + return cast(AsyncGuard[List], guard) + else: + return cast(AsyncGuard[Dict], guard) + + @classmethod + def from_pydantic( + cls, + output_class: ModelOrListOfModels, + *, + prompt: Optional[str] = None, # deprecate this too + instructions: Optional[str] = None, # deprecate this too + num_reasks: Optional[int] = None, + reask_prompt: Optional[str] = None, # deprecate this too + reask_instructions: Optional[str] = None, # deprecate this too + tracer: Optional[Tracer] = None, + name: Optional[str] = None, + description: Optional[str] = None, + ): + guard = super().from_pydantic( + output_class, + prompt=prompt, + instructions=instructions, + num_reasks=num_reasks, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + tracer=tracer, + name=name, + description=description, + ) + if guard._output_type == OutputTypes.LIST: + return cast(AsyncGuard[List], guard) + else: + return cast(AsyncGuard[Dict], guard) + + @classmethod + def from_string( + cls, + validators: Sequence[Validator], + *, + string_description: Optional[str] = None, + prompt: Optional[str] = None, # deprecate this too + instructions: Optional[str] = None, # deprecate this too + reask_prompt: Optional[str] = None, # deprecate this too + reask_instructions: Optional[str] = None, # deprecate this too + num_reasks: Optional[int] = None, + tracer: Optional[Tracer] = None, + name: Optional[str] = None, + description: Optional[str] = None, + ): + guard = super().from_string( + validators, + string_description=string_description, + prompt=prompt, + instructions=instructions, + reask_prompt=reask_prompt, + reask_instructions=reask_instructions, + num_reasks=num_reasks, + tracer=tracer, + name=name, + description=description, + ) + return cast(AsyncGuard[str], guard) + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional["AsyncGuard"]: + guard = super().from_dict(obj) + return cast(AsyncGuard, guard) + + def use( + self, + validator: UseValidatorSpec, + *args, + on: str = "output", + **kwargs, + ) -> "AsyncGuard": + guard = super().use(validator, *args, on=on, **kwargs) + return cast(AsyncGuard, guard) + + def use_many( + self, + *validators: UseManyValidatorSpec, + on: str = "output", + ) -> "AsyncGuard": + guard = super().use_many(*validators, on=on) # type: ignore + return cast(AsyncGuard, guard) + + async def _execute( self, - llm_api: Union[Callable, Callable[[Any], Awaitable[Any]]], + *args, + llm_api: Optional[Callable[..., Awaitable[Any]]] = None, + llm_output: Optional[str] = None, prompt_params: Optional[Dict] = None, num_reasks: Optional[int] = None, prompt: Optional[str] = None, instructions: Optional[str] = None, msg_history: Optional[List[Dict]] = None, - metadata: Optional[Dict] = None, + metadata: Optional[Dict], full_schema_reask: Optional[bool] = None, - *args, **kwargs, ) -> Union[ - Union[ValidationOutcome[OT], Iterable[ValidationOutcome[OT]]], + ValidationOutcome[OT], Awaitable[ValidationOutcome[OT]], + AsyncIterable[ValidationOutcome[OT]], ]: - """Call the LLM and validate the output. Pass an async LLM API to - return a coroutine. - - Args: - llm_api: The LLM API to 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. + self._fill_validator_map() + self._fill_validators() + metadata = metadata or {} + if not llm_api and not llm_output: + raise RuntimeError("'llm_api' or 'llm_output' must be provided!") + if not llm_output and llm_api and not (prompt or msg_history): + raise RuntimeError( + "'prompt' or 'msg_history' must be provided in order to call an LLM!" + ) - Returns: - The raw text output from the LLM and the validated output. - """ + # check if validator requirements are fulfilled + missing_keys = verify_metadata_requirements(metadata, self._validators) + if missing_keys: + raise ValueError( + f"Missing required metadata keys: {', '.join(missing_keys)}" + ) - async def __call( - self, - llm_api: Union[Callable, Callable[[Any], Awaitable[Any]]], + async def __exec( + self: AsyncGuard, + *args, + llm_api: Optional[Callable[..., Awaitable[Any]]], + llm_output: Optional[str] = None, prompt_params: Optional[Dict] = None, num_reasks: Optional[int] = None, prompt: Optional[str] = None, @@ -94,22 +214,23 @@ async def __call( msg_history: Optional[List[Dict]] = None, metadata: Optional[Dict] = None, full_schema_reask: Optional[bool] = None, - *args, **kwargs, - ): - if metadata is None: - metadata = {} + ) -> Union[ + ValidationOutcome[OT], + Awaitable[ValidationOutcome[OT]], + AsyncIterable[ValidationOutcome[OT]], + ]: + prompt_params = prompt_params or {} + metadata = metadata or {} if full_schema_reask is None: - full_schema_reask = self.base_model is not None - if prompt_params is None: - prompt_params = {} + full_schema_reask = self._base_model is not None - if not self._disable_tracer: + if self._allow_metrics_collection: # Create a new span for this guard call self._hub_telemetry.create_new_span( span_name="/guard_call", attributes=[ - ("guard_id", self._guard_id), + ("guard_id", self.id), ("user_id", self._user_id), ( "llm_api", @@ -117,10 +238,13 @@ async def __call( if (llm_api and hasattr(llm_api, "__name__")) else type(llm_api).__name__, ), - ("custom_reask_prompt", self.reask_prompt is not None), + ( + "custom_reask_prompt", + self._exec_opts.reask_prompt is not None, + ), ( "custom_reask_instructions", - self.reask_instructions is not None, + self._exec_opts.reask_instructions is not None, ), ], is_parent=True, # It will have children @@ -131,51 +255,52 @@ async def __call( set_tracer(self._tracer) set_tracer_context(self._tracer_context) - self.configure(num_reasks) - if self.num_reasks is None: + self._set_num_reasks(num_reasks=num_reasks) + if self._num_reasks is None: raise RuntimeError( "`num_reasks` is `None` after calling `configure()`. " "This should never happen." ) - input_prompt = prompt or (self.prompt._source if self.prompt else None) - input_instructions = instructions or ( - self.instructions._source if self.instructions else None - ) + input_prompt = prompt or self._exec_opts.prompt + input_instructions = instructions or self._exec_opts.instructions call_inputs = CallInputs( llm_api=llm_api, prompt=input_prompt, instructions=input_instructions, msg_history=msg_history, prompt_params=prompt_params, - num_reasks=self.num_reasks, + num_reasks=self._num_reasks, metadata=metadata, full_schema_reask=full_schema_reask, args=list(args), kwargs=kwargs, - stream=kwargs.get("stream"), ) - call_log = Call(inputs=call_inputs) - set_scope(str(id(call_log))) - self.history.push(call_log) if self._api_client is not None and model_is_supported_server_side( llm_api, *args, **kwargs ): result = self._call_server( + llm_output=llm_output, llm_api=llm_api, - num_reasks=self.num_reasks, + num_reasks=self._num_reasks, prompt_params=prompt_params, + metadata=metadata, full_schema_reask=full_schema_reask, - call_log=call_log, *args, **kwargs, ) + + # If the LLM API is async, return a coroutine else: - result = self._call_async( - llm_api, + call_log = Call(inputs=call_inputs) + set_scope(str(object_id(call_log))) + self.history.push(call_log) + result = await self._exec( + llm_api=llm_api, + llm_output=llm_output, prompt_params=prompt_params, - num_reasks=self.num_reasks, + num_reasks=self._num_reasks, prompt=prompt, instructions=instructions, msg_history=msg_history, @@ -188,38 +313,45 @@ async def __call( if inspect.isawaitable(result): return await result - return result + # TODO: Fix types once async streaming is implemented on server + return result # type: ignore guard_context = contextvars.Context() return await guard_context.run( - __call, + __exec, self, - llm_api, - prompt_params, - num_reasks, - prompt, - instructions, - msg_history, - metadata, - full_schema_reask, + llm_api=llm_api, + llm_output=llm_output, + prompt_params=prompt_params, + num_reasks=num_reasks, + prompt=prompt, + instructions=instructions, + msg_history=msg_history, + metadata=metadata, + full_schema_reask=full_schema_reask, *args, **kwargs, ) - async def _call_async( + async def _exec( self, - llm_api: Callable[[Any], Awaitable[Any]], - prompt_params: Dict, - num_reasks: int, + *args, + llm_api: Optional[Callable[[Any], Awaitable[Any]]], + llm_output: Optional[str] = None, + call_log: Call, + prompt_params: Dict, # Should be defined at this point + num_reasks: int = 0, # Should be defined at this point + metadata: Dict, # Should be defined at this point + full_schema_reask: bool = False, # Should be defined at this point prompt: Optional[str], instructions: Optional[str], msg_history: Optional[List[Dict]], - metadata: Dict, - full_schema_reask: bool, - call_log: Call, - *args, **kwargs, - ) -> Union[ValidationOutcome[OT], AsyncIterable[ValidationOutcome[OT]]]: + ) -> Union[ + ValidationOutcome[OT], + Awaitable[ValidationOutcome[OT]], + AsyncIterable[ValidationOutcome[OT]], + ]: """Call the LLM asynchronously and validate the output. Args: @@ -238,284 +370,220 @@ async def _call_async( Returns: The raw text output from the LLM and the validated output. """ - instructions_obj = instructions or self.rail.instructions - prompt_obj = prompt or self.rail.prompt - msg_history_obj = msg_history or [] - if prompt_obj is None: - if msg_history_obj is not None and not len(msg_history_obj): - raise RuntimeError( - "You must provide a prompt if msg_history is empty. " - "Alternatively, you can provide a prompt in the RAIL spec." - ) + api = ( + get_async_llm_ask(llm_api, *args, **kwargs) if llm_api is not None else None + ) if kwargs.get("stream", False): runner = AsyncStreamRunner( - instructions=instructions_obj, - prompt=prompt_obj, - msg_history=msg_history_obj, - api=get_async_llm_ask(llm_api, *args, **kwargs), - prompt_schema=self.rail.prompt_schema, - instructions_schema=self.rail.instructions_schema, - msg_history_schema=self.rail.msg_history_schema, - output_schema=self.rail.output_schema, + output_type=self._output_type, + output_schema=self.output_schema.to_dict(), num_reasks=num_reasks, + validation_map=self._validator_map, + prompt=prompt, + instructions=instructions, + msg_history=msg_history, + api=api, metadata=metadata, - base_model=self.base_model, + output=llm_output, + base_model=self._base_model, full_schema_reask=full_schema_reask, - disable_tracer=self._disable_tracer, + disable_tracer=(not self._allow_metrics_collection), + exec_options=self._exec_opts, ) # Here we have an async generator async_generator = runner.async_run( call_log=call_log, prompt_params=prompt_params ) return async_generator - else: runner = AsyncRunner( - instructions=instructions_obj, - prompt=prompt_obj, - msg_history=msg_history_obj, - api=get_async_llm_ask(llm_api, *args, **kwargs), - prompt_schema=self.rail.prompt_schema, - instructions_schema=self.rail.instructions_schema, - msg_history_schema=self.rail.msg_history_schema, - output_schema=self.rail.output_schema, + output_type=self._output_type, + output_schema=self.output_schema.to_dict(), num_reasks=num_reasks, + validation_map=self._validator_map, + prompt=prompt, + instructions=instructions, + msg_history=msg_history, + api=api, metadata=metadata, - base_model=self.base_model, + output=llm_output, + base_model=self._base_model, full_schema_reask=full_schema_reask, - disable_tracer=self._disable_tracer, + disable_tracer=(not self._allow_metrics_collection), + exec_options=self._exec_opts, ) + # Why are we using a different method here instead of just overriding? call = await runner.async_run( call_log=call_log, prompt_params=prompt_params ) return ValidationOutcome[OT].from_guard_history(call) - async def parse( + async def __call__( self, - llm_output: str, - metadata: Optional[Dict] = None, - llm_api: Optional[Callable] = None, - num_reasks: Optional[int] = None, + llm_api: Optional[Callable[..., Awaitable[Any]]] = None, + *args, prompt_params: Optional[Dict] = None, + num_reasks: Optional[int] = 1, + prompt: Optional[str] = None, + instructions: Optional[str] = None, + msg_history: Optional[List[Dict]] = None, + metadata: Optional[Dict] = None, full_schema_reask: Optional[bool] = None, - *args, **kwargs, - ) -> Union[ValidationOutcome[OT], Awaitable[ValidationOutcome[OT]]]: - """Alternate flow to using Guard where the llm_output is known. + ) -> Union[ + ValidationOutcome[OT], + Awaitable[ValidationOutcome[OT]], + AsyncIterable[ValidationOutcome[OT]], + ]: + """Call the LLM and validate the output. Pass an async LLM API to + return a coroutine. Args: - llm_output: The output being parsed and validated. - metadata: Metadata to pass to the validators. llm_api: The LLM API to call (e.g. openai.Completion.create or openai.Completion.acreate) - num_reasks: The max times to re-ask the LLM for invalid output. 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 validated response. This is either a string or a dictionary, - determined by the object schema defined in the RAILspec. + The raw text output from the LLM and the validated output. """ - async def __parse( - self, - llm_output: str, - metadata: Optional[Dict] = None, - llm_api: Optional[Callable] = None, - num_reasks: Optional[int] = None, - prompt_params: Optional[Dict] = None, - full_schema_reask: Optional[bool] = None, - *args, - **kwargs, - ): - final_num_reasks = ( - num_reasks if num_reasks is not None else 0 if llm_api is None else None - ) - - if not self._disable_tracer: - self._hub_telemetry.create_new_span( - span_name="/guard_parse", - attributes=[ - ("guard_id", self._guard_id), - ("user_id", self._user_id), - ( - "llm_api", - llm_api.__name__ - if (llm_api and hasattr(llm_api, "__name__")) - else type(llm_api).__name__, - ), - ("custom_reask_prompt", self.reask_prompt is not None), - ( - "custom_reask_instructions", - self.reask_instructions is not None, - ), - ], - is_parent=True, # It will have children - has_parent=False, # Has no parents - ) - - self.configure(final_num_reasks) - if self.num_reasks is None: + instructions = instructions or self._exec_opts.instructions + prompt = prompt or self._exec_opts.prompt + msg_history = msg_history or [] + if prompt is None: + if msg_history is not None and not len(msg_history): raise RuntimeError( - "`num_reasks` is `None` after calling `configure()`. " - "This should never happen." - ) - if full_schema_reask is None: - full_schema_reask = self.base_model is not None - metadata = metadata or {} - prompt_params = prompt_params or {} - - set_call_kwargs(kwargs) - set_tracer(self._tracer) - set_tracer_context(self._tracer_context) - - input_prompt = self.prompt._source if self.prompt else None - input_instructions = ( - self.instructions._source if self.instructions else None - ) - call_inputs = CallInputs( - llm_api=llm_api, - llm_output=llm_output, - prompt=input_prompt, - instructions=input_instructions, - prompt_params=prompt_params, - num_reasks=self.num_reasks, - metadata=metadata, - full_schema_reask=full_schema_reask, - args=list(args), - kwargs=kwargs, - stream=kwargs.get("stream"), - ) - call_log = Call(inputs=call_inputs) - set_scope(str(id(call_log))) - self.history.push(call_log) - - if self._api_client is not None and model_is_supported_server_side( - llm_api, *args, **kwargs - ): - return self._call_server( - llm_output=llm_output, - llm_api=llm_api, - num_reasks=self.num_reasks, - prompt_params=prompt_params, - full_schema_reask=full_schema_reask, - call_log=call_log, - *args, - **kwargs, + "You must provide a prompt if msg_history is empty. " + "Alternatively, you can provide a prompt in the Schema constructor." ) - return await self._async_parse( - llm_output, - metadata, - llm_api=llm_api, - num_reasks=self.num_reasks, - prompt_params=prompt_params, - full_schema_reask=full_schema_reask, - call_log=call_log, - *args, - **kwargs, - ) - - guard_context = contextvars.Context() - return await guard_context.run( - __parse, - self, - llm_output, - metadata, - llm_api, - num_reasks, - prompt_params, - full_schema_reask, + return await self._execute( *args, + llm_api=llm_api, + prompt_params=prompt_params, + num_reasks=num_reasks, + prompt=prompt, + instructions=instructions, + msg_history=msg_history, + metadata=metadata, + full_schema_reask=full_schema_reask, **kwargs, ) - async def _async_parse( + async def parse( self, llm_output: str, - metadata: Dict, - llm_api: Optional[Callable[[Any], Awaitable[Any]]], - num_reasks: int, - prompt_params: Dict, - full_schema_reask: bool, - call_log: Call, *args, + metadata: Optional[Dict] = None, + llm_api: Optional[Callable[..., Awaitable[Any]]] = None, + num_reasks: Optional[int] = None, + prompt_params: Optional[Dict] = None, + full_schema_reask: Optional[bool] = None, **kwargs, - ) -> ValidationOutcome[OT]: - """Alternate flow to using Guard where the llm_output is known. + ) -> Awaitable[ValidationOutcome[OT]]: + """Alternate flow to using AsyncGuard where the llm_output is known. Args: - llm_output: The output from the LLM. - llm_api: The LLM API to use to re-ask the LLM. + llm_output: The output being parsed and validated. + metadata: Metadata to pass to the validators. + llm_api: The LLM API to call + (e.g. openai.Completion.create or openai.Completion.acreate) num_reasks: The max times to re-ask the LLM for invalid output. + prompt_params: The parameters to pass to the prompt.format() method. + full_schema_reask: When reasking, whether to regenerate the full schema + or just the incorrect values. Returns: - The validated response. + The validated response. This is either a string or a dictionary, + determined by the object schema defined in the RAILspec. """ - runner = AsyncRunner( - instructions=kwargs.pop("instructions", None), - prompt=kwargs.pop("prompt", None), - msg_history=kwargs.pop("msg_history", None), - api=get_async_llm_ask(llm_api, *args, **kwargs) if llm_api else None, - prompt_schema=self.rail.prompt_schema, - instructions_schema=self.rail.instructions_schema, - msg_history_schema=self.rail.msg_history_schema, - output_schema=self.rail.output_schema, - num_reasks=num_reasks, + + final_num_reasks = ( + num_reasks + if num_reasks is not None + else self._num_reasks + if self._num_reasks is not None + else 0 + if llm_api is None + else 1 + ) + default_prompt = self._exec_opts.prompt if llm_api is not None else None + prompt = kwargs.pop("prompt", default_prompt) + + default_instructions = self._exec_opts.instructions if llm_api else None + instructions = kwargs.pop("instructions", default_instructions) + + default_msg_history = self._exec_opts.msg_history if llm_api else None + msg_history = kwargs.pop("msg_history", default_msg_history) + + return await self._execute( # type: ignore + *args, + llm_output=llm_output, + llm_api=llm_api, + prompt_params=prompt_params, + num_reasks=final_num_reasks, + prompt=prompt, + instructions=instructions, + msg_history=msg_history, metadata=metadata, - output=llm_output, - base_model=self.base_model, full_schema_reask=full_schema_reask, - disable_tracer=self._disable_tracer, + **kwargs, ) - call = await runner.async_run(call_log=call_log, prompt_params=prompt_params) - - return ValidationOutcome[OT].from_guard_history(call) async def _stream_server_call( - self, - *, - payload: Dict[str, Any], - llm_output: Optional[str] = None, - num_reasks: Optional[int] = None, - prompt_params: Optional[Dict] = None, - metadata: Optional[Dict] = {}, - full_schema_reask: Optional[bool] = True, - call_log: Optional[Call], + self, *, payload: Dict[str, Any] ) -> AsyncIterable[ValidationOutcome[OT]]: # TODO: Once server side supports async streaming, this function will need to # yield async generators, not generators if self._api_client: - validation_output: Optional[ValidationOutput] = None + validation_output: Optional[IValidationOutcome] = None response = self._api_client.stream_validate( guard=self, # type: ignore - payload=ValidatePayload.from_dict(payload), + payload=ValidatePayload.from_dict(payload), # type: ignore openai_api_key=get_call_kwarg("api_key"), ) for fragment in response: validation_output = fragment - if not validation_output: + if validation_output is None: yield ValidationOutcome[OT]( + call_id="0", # type: ignore raw_llm_output=None, validated_output=None, validation_passed=False, error="The response from the server was empty!", ) - yield ValidationOutcome[OT]( - raw_llm_output=validation_output.raw_llm_response, # type: ignore - validated_output=cast(OT, validation_output.validated_output), - validation_passed=validation_output.result, - ) + else: + validated_output = ( + cast(OT, validation_output.validated_output.actual_instance) + if validation_output.validated_output + else None + ) + yield ValidationOutcome[OT]( + call_id=validation_output.call_id, # type: ignore + raw_llm_output=validation_output.raw_llm_output, # type: ignore + validated_output=validated_output, + validation_passed=(validation_output.validation_passed is True), + ) if validation_output: - self._construct_history_from_server_response( - validation_output=validation_output, - llm_output=llm_output, - num_reasks=num_reasks, - prompt_params=prompt_params, - metadata=metadata, - full_schema_reask=full_schema_reask, - call_log=call_log, + guard_history = self._api_client.get_history( + self.name, validation_output.call_id + ) + self.history.extend( + [Call.from_interface(call) for call in guard_history] ) else: - raise ValueError("Guard does not have an api client!") + raise ValueError("AsyncGuard does not have an api client!") + + async def validate( + self, llm_output: str, *args, **kwargs + ) -> Awaitable[ValidationOutcome[OT]]: + return await self.parse(llm_output=llm_output, *args, **kwargs) diff --git a/guardrails/call_tracing/__init__.py b/guardrails/call_tracing/__init__.py new file mode 100644 index 000000000..cfc312cc5 --- /dev/null +++ b/guardrails/call_tracing/__init__.py @@ -0,0 +1,12 @@ +"""For tracing (logging) and reporting the timing of Guard and Validator calls. + +sqlite_trace_handler defines most of the actual implementation methods. +trace_handler provides the singleton that's used for fast global access +across threads. tracer_mixin defines the interface and can act as a +noop. trace_entry is just a helpful dataclass. +""" + +from guardrails.call_tracing.trace_entry import GuardTraceEntry +from guardrails.call_tracing.trace_handler import TraceHandler + +__all__ = ["GuardTraceEntry", "TraceHandler"] diff --git a/guardrails/call_tracing/sqlite_trace_handler.py b/guardrails/call_tracing/sqlite_trace_handler.py new file mode 100644 index 000000000..1d555d6fd --- /dev/null +++ b/guardrails/call_tracing/sqlite_trace_handler.py @@ -0,0 +1,235 @@ +"""sqlite_trace_handler.py. + +This is the metaphorical bread and butter of our tracing implementation, +or at least the butter. It wraps a SQLite database and configures it to +be 'agreeable' in multithreaded situations. Normally, when sharing +across threads and instances one should consider using a larger database +solution like Postgres, but in this case we only care about _supporting_ +writing from multiple places. We don't expect it will be the norm. We +care about (1) not negatively impacting performance, (2) not crashing +when used in unusual ways, and (3) not losing data when possible. + +The happy path should be reasonably performant. The unhappy path should +not crash. + +The other part of the multithreaded support comes from the public +trace_handler, which uses a singleton pattern to only have a single +instance of the database per-thread. If we _do_ somehow end up shared +across threads, the journaling settings and writeahead should protect us +from odd behavior. +""" + +import datetime +import os +import sqlite3 +import time +from dataclasses import asdict +from typing import Iterator + +from guardrails.call_tracing.trace_entry import GuardTraceEntry +from guardrails.call_tracing.tracer_mixin import TracerMixin +from guardrails.classes.validation.validator_logs import ValidatorLogs +from guardrails.utils.casting_utils import to_string + + +LOG_RETENTION_LIMIT = 100000 +TIME_BETWEEN_CLEANUPS = 10.0 # Seconds + + +# These adapters make it more convenient to add data into our log DB: +# Handle timestamp -> sqlite map: +def adapt_datetime(val): + """Adapt datetime.datetime to Unix timestamp.""" + # return val.isoformat() # If we want to go to datetime/isoformat... + return int(val.timestamp()) + + +sqlite3.register_adapter(datetime.datetime, adapt_datetime) + + +def convert_timestamp(val): + """Convert Unix epoch timestamp to datetime.datetime object.""" + # To go to datetime.datetime: + # return datetime.datetime.fromisoformat(val.decode()) + return datetime.datetime.fromtimestamp(int(val)) + + +sqlite3.register_converter("timestamp", convert_timestamp) + + +# This structured handler shouldn't be used directly, since it's touching a SQLite db. +# Instead, use the singleton or the async singleton. +class SQLiteTraceHandler(TracerMixin): + CREATE_COMMAND = """ + CREATE TABLE IF NOT EXISTS guard_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + guard_name TEXT, + start_time REAL, + end_time REAL, + prevalidate_text TEXT, + postvalidate_text TEXT, + exception_message TEXT + ); + """ + INSERT_COMMAND = """ + INSERT INTO guard_logs ( + guard_name, start_time, end_time, prevalidate_text, postvalidate_text, + exception_message + ) VALUES ( + :guard_name, :start_time, :end_time, :prevalidate_text, :postvalidate_text, + :exception_message + ); + """ + + def __init__(self, log_path: os.PathLike, read_mode: bool): + self._log_path = log_path # Read-only value. + self.last_cleanup = time.time() + self.readonly = read_mode + if read_mode: + self.db = SQLiteTraceHandler._get_read_connection(log_path) + else: + self.db = SQLiteTraceHandler._get_write_connection(log_path) + + @property + def log_path(self): + return self._log_path + + @classmethod + def _get_write_connection(cls, log_path: os.PathLike) -> sqlite3.Connection: + try: + db = sqlite3.connect( + log_path, + isolation_level=None, + check_same_thread=False, + ) + db.execute("PRAGMA journal_mode = wal") + db.execute("PRAGMA synchronous = OFF") + # isolation_level = None and pragma WAL means we can READ from the DB + # while threads using it are writing. Synchronous off puts us on the + # highway to the danger zone, depending on how willing we are to lose log + # messages in the event of a guard crash. + except sqlite3.OperationalError as e: + # logging.exception("Unable to connect to guard log handler.") + raise e + with db: + db.execute(SQLiteTraceHandler.CREATE_COMMAND) + return db + + @classmethod + def _get_read_connection(cls, log_path: os.PathLike) -> sqlite3.Connection: + # A bit of a hack to open in read-only mode... + db = sqlite3.connect( + "file:" + str(log_path) + "?mode=ro", isolation_level=None, uri=True + ) + db.row_factory = sqlite3.Row + return db + + def _truncate(self, force: bool = False, keep_n: int = LOG_RETENTION_LIMIT): + assert not self.readonly + now = time.time() + if force or (now - self.last_cleanup > TIME_BETWEEN_CLEANUPS): + self.last_cleanup = now + self.db.execute( + """ + DELETE FROM guard_logs + WHERE id < ( + SELECT id FROM guard_logs ORDER BY id DESC LIMIT 1 OFFSET ? + ); + """, + (keep_n,), + ) + + def log( + self, + guard_name: str, + start_time: float, + end_time: float, + prevalidate_text: str, + postvalidate_text: str, + exception_text: str, + ): + assert not self.readonly + with self.db: + self.db.execute( + SQLiteTraceHandler.INSERT_COMMAND, + dict( + guard_name=guard_name, + start_time=start_time, + end_time=end_time, + prevalidate_text=prevalidate_text, + postvalidate_text=postvalidate_text, + exception_message=exception_text, + ), + ) + self._truncate() + + def log_entry(self, guard_log_entry: GuardTraceEntry): + assert not self.readonly + with self.db: + self.db.execute(SQLiteTraceHandler.INSERT_COMMAND, asdict(guard_log_entry)) + self._truncate() + + def log_validator(self, vlog: ValidatorLogs): + assert not self.readonly + maybe_outcome = ( + str(vlog.validation_result.outcome) + if ( + vlog.validation_result is not None + and hasattr(vlog.validation_result, "outcome") + ) + else "" + ) + with self.db: + self.db.execute( + SQLiteTraceHandler.INSERT_COMMAND, + dict( + guard_name=vlog.validator_name, + start_time=vlog.start_time if vlog.start_time else None, + end_time=vlog.end_time if vlog.end_time else 0.0, + prevalidate_text=to_string(vlog.value_before_validation), + postvalidate_text=to_string(vlog.value_after_validation), + exception_message=maybe_outcome, + ), + ) + self._truncate() + + def tail_logs( + self, start_offset_idx: int = 0, follow: bool = False + ) -> Iterator[GuardTraceEntry]: + """Returns an iterator to generate GuardLogEntries. @param + start_offset_idx : Start printing entries after this IDX. If. + + negative, this will instead start printing the LAST + start_offset_idx entries. @param follow : If follow is True, + will re-check the database for new entries after the first batch + is complete. If False (default), will return when entries are + exhausted. + """ + last_idx = start_offset_idx + cursor = self.db.cursor() + if last_idx < 0: + # We're indexing from the end, so do a quick check. + cursor.execute( + "SELECT id FROM guard_logs ORDER BY id DESC LIMIT 1 OFFSET ?;", + (-last_idx,), + ) + for row in cursor: + last_idx = row["id"] + sql = """ + SELECT + id, guard_name, start_time, end_time, prevalidate_text, + postvalidate_text, exception_message + FROM guard_logs + WHERE id > ? + ORDER BY start_time; + """ + cursor.execute(sql, (last_idx,)) + while True: + for row in cursor: + last_entry = GuardTraceEntry(**row) + last_idx = last_entry.id + yield last_entry + if not follow: + return + # If we're here we've run out of entries to tail. Fetch more: + cursor.execute(sql, (last_idx,)) diff --git a/guardrails/call_tracing/trace_entry.py b/guardrails/call_tracing/trace_entry.py new file mode 100644 index 000000000..6f807a9f1 --- /dev/null +++ b/guardrails/call_tracing/trace_entry.py @@ -0,0 +1,25 @@ +"""trace_entry.py. + +GuardTraceEntry is a dataclass which doesn't explicitly define the +schema of our logs, but serves as a nice, easy-to-use dataclass for when +we want to manipulate things programmatically. If performance and +filtering is a concern, it's probably worth writing the SQL directly +instead of filtering these in a for-loop. +""" + +from dataclasses import dataclass + + +@dataclass +class GuardTraceEntry: + id: int = -1 + guard_name: str = "" + start_time: float = 0.0 + end_time: float = 0.0 + prevalidate_text: str = "" + postvalidate_text: str = "" + exception_message: str = "" + + @property + def timedelta(self): + return self.end_time - self.start_time diff --git a/guardrails/call_tracing/trace_handler.py b/guardrails/call_tracing/trace_handler.py new file mode 100644 index 000000000..1e689f2d0 --- /dev/null +++ b/guardrails/call_tracing/trace_handler.py @@ -0,0 +1,71 @@ +"""trace_handler.py. + +A set of tools to track the behavior of guards, specifically with the intent of +collating the pre/post validation text and timing of guard calls. Uses a singleton to +share write access to a SQLite database across threads. + +By default, logs will be created in a temporary directory. This can be overridden by +setting GUARDRAILS_LOG_FILE_PATH in the environment. tracehandler.log_path will give +the full path of the current log file. + +# Reading logs (basic): +>>> reader = TraceHandler.get_reader() +>>> for t in reader.tail_logs(): +>>> print(t) + +# Reading logs (advanced): +>>> reader = TraceHandler.get_reader() +>>> reader.db.execute("SELECT * FROM guard_logs;") # Arbitrary SQL support. + +# Saving logs +>>> writer = TraceHandler() +>>> writer.log( +>>> "my_guard_name", 0.0, 1.0, "Raw LLM Output Text", "Sanitized", "exception?" +>>> ) +""" + +import os +import tempfile +import threading + +from guardrails.call_tracing.sqlite_trace_handler import SQLiteTraceHandler +from guardrails.call_tracing.tracer_mixin import TracerMixin + +# TODO: We should read this from guardrailsrc. +LOG_FILENAME = "guardrails_calls.db" +LOGFILE_PATH = os.environ.get( + "GUARDRAILS_LOG_FILE_PATH", # Document this environment variable. + os.path.join(tempfile.gettempdir(), LOG_FILENAME), +) + + +class TraceHandler(TracerMixin): + """TraceHandler wraps the internal _SQLiteTraceHandler to make it multi- + thread safe. + + Coupled with some write ahead journaling in the _SyncTrace internal, + we have a faux-multi-write multi-read interface for SQLite. + """ + + _instance = None + _lock = threading.Lock() + + def __new__(cls): + if cls._instance is None: + # We run two 'if None' checks so we don't have to call the mutex check for + # the cases where there's obviously no handler. Only do a check if there + # MIGHT not be a handler instantiated. + with cls._lock: + if cls._instance is None: + cls._instance = cls._create() + return cls._instance + + @classmethod + def _create(cls, path: os.PathLike = LOGFILE_PATH) -> TracerMixin: # type: ignore + return SQLiteTraceHandler(path, read_mode=False) + # To disable logging: + # return _BaseTraceHandler(path, read_mode=False) + + @classmethod + def get_reader(cls, path: os.PathLike = LOGFILE_PATH) -> TracerMixin: # type: ignore + return SQLiteTraceHandler(path, read_mode=True) diff --git a/guardrails/call_tracing/tracer_mixin.py b/guardrails/call_tracing/tracer_mixin.py new file mode 100644 index 000000000..76b1684f3 --- /dev/null +++ b/guardrails/call_tracing/tracer_mixin.py @@ -0,0 +1,34 @@ +"""tracer_mixin.py. + +This file defines our preferred tracer interface. It has a side effect +of acting as a 'noop' when we want to benchmark performance of a tracer. +""" + +import os +from typing import Iterator + +from guardrails.call_tracing.trace_entry import GuardTraceEntry +from guardrails.classes.validation.validator_logs import ValidatorLogs + + +class TracerMixin: + """The pads out the methods but is otherwise a noop.""" + + def __init__(self, log_path: os.PathLike, read_mode: bool): + self.db = None + + def log(self, *args, **kwargs): + pass + + def log_entry(self, guard_log_entry: GuardTraceEntry): + pass + + def log_validator(self, vlog: ValidatorLogs): + pass + + def tail_logs( + self, + start_offset_idx: int = 0, + follow: bool = False, + ) -> Iterator[GuardTraceEntry]: + yield from [] diff --git a/guardrails/classes/__init__.py b/guardrails/classes/__init__.py index 066311801..16bf9ac0e 100644 --- a/guardrails/classes/__init__.py +++ b/guardrails/classes/__init__.py @@ -1,6 +1,21 @@ from guardrails.classes.credentials import Credentials from guardrails.classes.input_type import InputType from guardrails.classes.output_type import OT +from guardrails.classes.validation.validation_result import ( + ValidationResult, + PassResult, + FailResult, + ErrorSpan, +) from guardrails.classes.validation_outcome import ValidationOutcome -__all__ = ["ValidationOutcome", "OT", "InputType", "Credentials"] +__all__ = [ + "Credentials", + "ErrorSpan", + "InputType", + "OT", + "ValidationResult", + "PassResult", + "FailResult", + "ValidationOutcome", +] diff --git a/guardrails/classes/credentials.py b/guardrails/classes/credentials.py index cde58a199..093480b79 100644 --- a/guardrails/classes/credentials.py +++ b/guardrails/classes/credentials.py @@ -13,6 +13,7 @@ class Credentials(Serializeable): token: Optional[str] = None no_metrics: Optional[bool] = False enable_metrics: Optional[bool] = True + use_remote_inferencing: Optional[bool] = True @staticmethod def _to_bool(value: str) -> Optional[bool]: @@ -36,7 +37,7 @@ def from_rc_file(logger: Optional[logging.Logger] = None) -> "Credentials": for line in filtered_lines: line_content = line.split("=", 1) if len(line_content) != 2: - logger.warn( + logger.warning( """ Invalid line found in .guardrailsrc file! All lines in this file should follow the format: key=value diff --git a/guardrails/classes/execution/__init__.py b/guardrails/classes/execution/__init__.py new file mode 100644 index 000000000..f0e5aef0a --- /dev/null +++ b/guardrails/classes/execution/__init__.py @@ -0,0 +1,3 @@ +from guardrails.classes.execution.guard_execution_options import GuardExecutionOptions + +__all__ = ["GuardExecutionOptions"] diff --git a/guardrails/classes/execution/guard_execution_options.py b/guardrails/classes/execution/guard_execution_options.py new file mode 100644 index 000000000..f46f68e88 --- /dev/null +++ b/guardrails/classes/execution/guard_execution_options.py @@ -0,0 +1,12 @@ +from typing import Dict, List, Optional +from dataclasses import dataclass + + +@dataclass +class GuardExecutionOptions: + prompt: Optional[str] = None + instructions: Optional[str] = None + msg_history: Optional[List[Dict]] = None + reask_prompt: Optional[str] = None + reask_instructions: Optional[str] = None + num_reasks: Optional[int] = None diff --git a/guardrails/classes/generic/__init__.py b/guardrails/classes/generic/__init__.py index fd1909521..52f50b212 100644 --- a/guardrails/classes/generic/__init__.py +++ b/guardrails/classes/generic/__init__.py @@ -1,4 +1,5 @@ +from guardrails.classes.generic.arbitrary_model import ArbitraryModel from guardrails.classes.generic.serializeable import Serializeable from guardrails.classes.generic.stack import Stack -__all__ = ["Stack", "Serializeable"] +__all__ = ["ArbitraryModel", "Stack", "Serializeable"] diff --git a/guardrails/classes/generic/arbitrary_model.py b/guardrails/classes/generic/arbitrary_model.py new file mode 100644 index 000000000..d26def657 --- /dev/null +++ b/guardrails/classes/generic/arbitrary_model.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel, ConfigDict + + +class ArbitraryModel(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) diff --git a/guardrails/classes/history/call.py b/guardrails/classes/history/call.py index 6598e3c12..99252830f 100644 --- a/guardrails/classes/history/call.py +++ b/guardrails/classes/history/call.py @@ -1,31 +1,34 @@ -from typing import Dict, Optional, Union - -from pydantic import Field, PrivateAttr +from typing import Any, Dict, List, Optional, Union +from builtins import id as object_id +from pydantic import Field from rich.panel import Panel from rich.pretty import pretty_repr from rich.tree import Tree -from typing_extensions import deprecated +from guardrails_api_client import Call as ICall +from guardrails.actions.filter import Filter +from guardrails.actions.refrain import Refrain +from guardrails.actions.reask import merge_reask_output from guardrails.classes.generic.stack import Stack from guardrails.classes.history.call_inputs import CallInputs from guardrails.classes.history.iteration import Iteration +from guardrails.classes.generic.arbitrary_model import ArbitraryModel +from guardrails.classes.validation.validation_result import ValidationResult from guardrails.constants import error_status, fail_status, not_run_status, pass_status from guardrails.prompt.instructions import Instructions from guardrails.prompt.prompt import Prompt -from guardrails.utils.logs_utils import ValidatorLogs, merge_reask_output -from guardrails.utils.pydantic_utils import ArbitraryModel -from guardrails.utils.reask_utils import ( +from guardrails.classes.validation.validator_logs import ValidatorLogs +from guardrails.actions.reask import ( ReAsk, gather_reasks, sub_reasks_with_fixed_values, ) -from guardrails.utils.safe_get import get_value_from_path -from guardrails.validator_base import Filter, Refrain, ValidationResult +from guardrails.schema.parser import get_value_from_path # We can't inherit from Iteration because python # won't let you override a class attribute with a managed attribute -class Call(ArbitraryModel): +class Call(ICall, ArbitraryModel): iterations: Stack[Iteration] = Field( description="A stack of iterations for each" "step/reask that occurred during this call." @@ -33,7 +36,10 @@ class Call(ArbitraryModel): inputs: CallInputs = Field( description="The inputs as passed in to Guard.__call__ or Guard.parse" ) - _exception: Optional[Exception] = PrivateAttr() + exception: Optional[Exception] = Field( + description="The exception that interrupted the run.", + default=None, + ) # Prevent Pydantic from changing our types # Without this, Pydantic casts iterations to a list @@ -43,16 +49,13 @@ def __init__( inputs: Optional[CallInputs] = None, exception: Optional[Exception] = None, ): + call_id = str(object_id(self)) iterations = iterations or Stack() inputs = inputs or CallInputs() - super().__init__( - iterations=iterations, # type: ignore - inputs=inputs, # type: ignore - _exception=exception, # type: ignore - ) + super().__init__(id=call_id, iterations=iterations, inputs=inputs) # type: ignore - pyright doesn't understand pydantic overrides self.iterations = iterations self.inputs = inputs - self._exception = exception + self.exception = exception @property def prompt(self) -> Optional[str]: @@ -193,21 +196,13 @@ def raw_outputs(self) -> Stack[str]: ) @property - def parsed_outputs(self) -> Stack[Union[str, Dict]]: + def parsed_outputs(self) -> Stack[Union[str, List, Dict]]: """The outputs from the LLM after undergoing parsing but before validation.""" return Stack(*[i.outputs.parsed_output for i in self.iterations]) @property - @deprecated( - """'Call.validation_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'validation_response' instead.""" - ) - def validation_output(self) -> Optional[Union[str, Dict, ReAsk]]: - return self.validation_response - - @property - def validation_response(self) -> Optional[Union[str, Dict, ReAsk]]: + def validation_response(self) -> Optional[Union[str, List, Dict, ReAsk]]: """The aggregated responses from the validation process across all iterations within the current call. @@ -252,7 +247,7 @@ def validation_response(self) -> Optional[Union[str, Dict, ReAsk]]: return merged_validation_responses @property - def fixed_output(self) -> Optional[Union[str, Dict]]: + def fixed_output(self) -> Optional[Union[str, List, Dict]]: """The cumulative output from the validation process across all current iterations with any automatic fixes applied. @@ -261,7 +256,7 @@ def fixed_output(self) -> Optional[Union[str, Dict]]: return sub_reasks_with_fixed_values(self.validation_response) @property - def guarded_output(self) -> Optional[Union[str, Dict]]: + def guarded_output(self) -> Optional[Union[str, List, Dict]]: """The complete validated output after all stages of validation are completed. @@ -293,18 +288,6 @@ def guarded_output(self) -> Optional[Union[str, Dict]]: if all_noop: return last_iteration.guarded_output - @property - @deprecated( - """'Call.validated_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'guarded_output' instead.""" - ) - def validated_output(self) -> Optional[Union[str, Dict]]: - """The output from the LLM after undergoing validation. - - This will only have a value if the Guard is in a passing state. - """ - return self.guarded_output - @property def reasks(self) -> Stack[ReAsk]: """Reasks generated during validation that could not be automatically @@ -329,21 +312,12 @@ def validator_logs(self) -> Stack[ValidatorLogs]: def error(self) -> Optional[str]: """The error message from any exception that raised and interrupted the run.""" - if self._exception: - return str(self._exception) + if self.exception: + return str(self.exception) elif self.iterations.empty(): return None return self.iterations.last.error # type: ignore - @property - def exception(self) -> Optional[Exception]: - """The exception that interrupted the run.""" - if self._exception: - return self._exception - elif self.iterations.empty(): - return None - return self.iterations.last.exception # type: ignore - @property def failed_validations(self) -> Stack[ValidatorLogs]: """The validator logs for any validations that failed during the @@ -420,3 +394,36 @@ def tree(self) -> Tree: def __str__(self) -> str: return pretty_repr(self) + + def to_interface(self) -> ICall: + return ICall( + id=self.id, + iterations=[i.to_interface() for i in self.iterations], + inputs=self.inputs.to_interface(), + exception=self.error, + ) + + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_call: ICall) -> "Call": + iterations = Stack( + *[Iteration.from_interface(i) for i in (i_call.iterations or [])] + ) + inputs = ( + CallInputs.from_interface(i_call.inputs) if i_call.inputs else CallInputs() + ) + exception = Exception(i_call.exception) if i_call.exception else None + call_inst = cls(iterations=iterations, inputs=inputs, exception=exception) + call_inst.id = i_call.id + return call_inst + + # TODO: Necessary to GET /guards/{guard_name}/history/{call_id} + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "Call": + i_call = ICall.from_dict(obj) + + if i_call: + return cls.from_interface(i_call) + return Call() diff --git a/guardrails/classes/history/call_inputs.py b/guardrails/classes/history/call_inputs.py index 2837afb0e..bc99a4905 100644 --- a/guardrails/classes/history/call_inputs.py +++ b/guardrails/classes/history/call_inputs.py @@ -2,10 +2,12 @@ from pydantic import Field +from guardrails_api_client import CallInputs as ICallInputs from guardrails.classes.history.inputs import Inputs +from guardrails.classes.generic.arbitrary_model import ArbitraryModel -class CallInputs(Inputs): +class CallInputs(Inputs, ICallInputs, ArbitraryModel): llm_api: Optional[Callable[[Any], Awaitable[Any]]] = Field( description="The LLM function provided by the user" "during Guard.__call__ or Guard.parse.", @@ -25,3 +27,45 @@ class CallInputs(Inputs): description="Additional keyword-arguments for the LLM as provided by the user.", default_factory=dict, ) + + def to_interface(self) -> ICallInputs: + inputs = super().to_interface().to_dict() or {} + inputs["args"] = self.args + # TODO: Better way to prevent creds from being logged, + # if they're passed in as kwargs to the LLM + redacted_kwargs = {} + for k, v in self.kwargs.items(): + if ("key" in k.lower() or "token" in k.lower()) and isinstance(v, str): + redaction_length = len(v) - 4 + stars = "*" * redaction_length + redacted_kwargs[k] = f"{stars}{v[-4:]}" + else: + redacted_kwargs[k] = v + inputs["kwargs"] = redacted_kwargs + return ICallInputs(**inputs) + + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_call_inputs: ICallInputs) -> "CallInputs": + return cls( + llm_api=None, + llm_output=i_call_inputs.llm_output, + instructions=i_call_inputs.instructions, + prompt=i_call_inputs.prompt, + msg_history=i_call_inputs.msg_history, + prompt_params=i_call_inputs.prompt_params, + num_reasks=i_call_inputs.num_reasks, + metadata=i_call_inputs.metadata, + full_schema_reask=(i_call_inputs.full_schema_reask is True), + stream=(i_call_inputs.stream is True), + args=(i_call_inputs.args or []), + kwargs=(i_call_inputs.kwargs or {}), + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]): + i_call_inputs = ICallInputs.from_dict(obj) or ICallInputs() + + return cls.from_interface(i_call_inputs) diff --git a/guardrails/classes/history/inputs.py b/guardrails/classes/history/inputs.py index 07a34920e..f9ee44423 100644 --- a/guardrails/classes/history/inputs.py +++ b/guardrails/classes/history/inputs.py @@ -2,13 +2,14 @@ from pydantic import Field +from guardrails_api_client import Inputs as IInputs +from guardrails.classes.generic.arbitrary_model import ArbitraryModel from guardrails.llm_providers import PromptCallableBase from guardrails.prompt.instructions import Instructions from guardrails.prompt.prompt import Prompt -from guardrails.utils.pydantic_utils import ArbitraryModel -class Inputs(ArbitraryModel): +class Inputs(IInputs, ArbitraryModel): llm_api: Optional[PromptCallableBase] = Field( description="The constructed class for calling the LLM.", default=None ) @@ -33,7 +34,7 @@ class Inputs(ArbitraryModel): "that will be formatted into the final LLM prompt.", default=None, ) - num_reasks: int = Field( + num_reasks: Optional[int] = Field( description="The total number of reasks allowed; user provided or defaulted.", default=None, ) @@ -41,7 +42,7 @@ class Inputs(ArbitraryModel): description="The metadata provided by the user to be used during validation.", default=None, ) - full_schema_reask: bool = Field( + full_schema_reask: Optional[bool] = Field( description="Whether to perform reasks across the entire schema" "or at the field level.", default=None, @@ -50,3 +51,78 @@ class Inputs(ArbitraryModel): description="Whether to use streaming.", default=False, ) + + def to_interface(self) -> IInputs: + serialized_msg_history = None + if self.msg_history: + serialized_msg_history = [] + for msg in self.msg_history: + ser_msg = {**msg} + content = ser_msg.get("content") + if content: + ser_msg["content"] = ( + content.source if isinstance(content, Prompt) else content + ) + serialized_msg_history.append(ser_msg) + + instructions = ( + self.instructions.source + if isinstance(self.instructions, Instructions) + else self.instructions + ) + + prompt = self.prompt.source if isinstance(self.prompt, Prompt) else self.prompt + + return IInputs( + llm_api=str(self.llm_api) if self.llm_api else None, # type: ignore - pyright doesn't understand aliases + llm_output=self.llm_output, # type: ignore - pyright doesn't understand aliases + instructions=instructions, + prompt=prompt, + msg_history=serialized_msg_history, # type: ignore - pyright doesn't understand aliases + prompt_params=self.prompt_params, # type: ignore - pyright doesn't understand aliases + num_reasks=self.num_reasks, # type: ignore - pyright doesn't understand aliases + metadata=self.metadata, + full_schema_reask=self.full_schema_reask, # type: ignore - pyright doesn't understand aliases + stream=self.stream, + ) + + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_inputs: IInputs) -> "Inputs": + deserialized_msg_history = None + if i_inputs.msg_history: + deserialized_msg_history = [] + for msg in i_inputs.msg_history: + ser_msg = {**msg} + content = ser_msg.get("content") + if content: + ser_msg["content"] = Prompt(content) + deserialized_msg_history.append(ser_msg) + + instructions = ( + Instructions(i_inputs.instructions) if i_inputs.instructions else None + ) + + prompt = Prompt(i_inputs.prompt) if i_inputs.prompt else None + num_reasks = ( + int(i_inputs.num_reasks) if i_inputs.num_reasks is not None else None + ) + return cls( + llm_api=None, + llm_output=i_inputs.llm_output, + instructions=instructions, + prompt=prompt, + msg_history=deserialized_msg_history, + prompt_params=i_inputs.prompt_params, + num_reasks=num_reasks, + metadata=i_inputs.metadata, + full_schema_reask=(i_inputs.full_schema_reask is True), + stream=(i_inputs.stream is True), + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "Inputs": + i_inputs = IInputs.from_dict(obj) or IInputs() + return cls.from_interface(i_inputs) diff --git a/guardrails/classes/history/iteration.py b/guardrails/classes/history/iteration.py index a8fb67ada..c440c7e95 100644 --- a/guardrails/classes/history/iteration.py +++ b/guardrails/classes/history/iteration.py @@ -1,24 +1,24 @@ -from typing import Dict, List, Optional, Sequence, Union - +from typing import Any, Dict, List, Optional, Sequence, Union +from builtins import id as object_id from pydantic import Field from rich.console import Group from rich.panel import Panel from rich.pretty import pretty_repr from rich.table import Table -from typing_extensions import deprecated +from guardrails_api_client import Iteration as IIteration from guardrails.classes.generic.stack import Stack from guardrails.classes.history.inputs import Inputs from guardrails.classes.history.outputs import Outputs +from guardrails.classes.generic.arbitrary_model import ArbitraryModel from guardrails.logger import get_scope_handler from guardrails.prompt.prompt import Prompt -from guardrails.utils.logs_utils import ValidatorLogs -from guardrails.utils.pydantic_utils import ArbitraryModel -from guardrails.utils.reask_utils import ReAsk -from guardrails.validator_base import ErrorSpan +from guardrails.classes.validation.validator_logs import ValidatorLogs +from guardrails.actions.reask import ReAsk +from guardrails.classes.validation.validation_result import ErrorSpan -class Iteration(ArbitraryModel): +class Iteration(IIteration, ArbitraryModel): # I think these should be containered since their names slightly overlap with # outputs, but could be convinced otherwise inputs: Inputs = Field( @@ -29,6 +29,26 @@ class Iteration(ArbitraryModel): description="The outputs from the iteration/step.", default_factory=Outputs ) + def __init__( + self, + call_id: str, + index: int, + inputs: Optional[Inputs] = None, + outputs: Optional[Outputs] = None, + ): + iteration_id = str(object_id(self)) + inputs = inputs or Inputs() + outputs = outputs or Outputs() + super().__init__( + id=iteration_id, + call_id=call_id, # type: ignore + index=index, + inputs=inputs, + outputs=outputs, + ) + self.inputs = inputs + self.outputs = outputs + @property def logs(self) -> Stack[str]: """Returns the logs from this iteration as a stack.""" @@ -72,13 +92,13 @@ def raw_output(self) -> Optional[str]: return self.outputs.raw_output @property - def parsed_output(self) -> Optional[Union[str, Dict]]: + def parsed_output(self) -> Optional[Union[str, List, Dict]]: """The output from the LLM after undergoing parsing but before validation.""" return self.outputs.parsed_output @property - def validation_response(self) -> Optional[Union[ReAsk, str, Dict]]: + def validation_response(self) -> Optional[Union[ReAsk, str, List, Dict]]: """The response from a single stage of validation. Validation response is the output of a single stage of validation @@ -90,19 +110,7 @@ def validation_response(self) -> Optional[Union[ReAsk, str, Dict]]: return self.outputs.validation_response @property - @deprecated( - """'Iteration.validation_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'validation_response' instead.""" - ) - def validation_output(self) -> Optional[Union[ReAsk, str, Dict]]: - """The output from the validation process. - - Could be a combination of valid output and ReAsks - """ - return self.validation_response - - @property - def guarded_output(self) -> Optional[Union[str, Dict]]: + def guarded_output(self) -> Optional[Union[str, List, Dict]]: """Any valid values after undergoing validation. Some values in the validated output may be "fixed" values that @@ -111,19 +119,6 @@ def guarded_output(self) -> Optional[Union[str, Dict]]: """ return self.outputs.guarded_output - @property - @deprecated( - """'Iteration.validated_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'guarded_output' instead.""" - ) - def validated_output(self) -> Optional[Union[str, Dict]]: - """The valid output from the LLM after undergoing validation. - - Could be only a partial structure if field level reasks occur. - Could contain fixed values. - """ - return self.outputs.guarded_output - @property def reasks(self) -> Sequence[ReAsk]: """Reasks generated during validation. @@ -241,3 +236,45 @@ def create_msg_history_table( def __str__(self) -> str: return pretty_repr(self) + + def to_interface(self) -> IIteration: + return IIteration( + id=self.id, + call_id=self.call_id, # type: ignore + index=self.index, + inputs=self.inputs.to_interface(), + outputs=self.outputs.to_interface(), + ) + + def to_dict(self) -> Dict: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_iteration: IIteration) -> "Iteration": + inputs = ( + Inputs.from_interface(i_iteration.inputs) if i_iteration.inputs else None + ) + outputs = ( + Outputs.from_interface(i_iteration.outputs) if i_iteration.outputs else None + ) + iteration = cls( + call_id=i_iteration.call_id, + index=i_iteration.index, + inputs=inputs, + outputs=outputs, + ) + iteration.id = i_iteration.id + return iteration + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "Iteration": + id = obj.get("id", "0") + call_id = obj.get("callId", obj.get("call_id", "0")) + index = obj.get("index", 0) + i_iteration = IIteration.from_dict(obj) or IIteration( + id=id, + call_id=call_id, # type: ignore + index=index, # type: ignore + ) + + return cls.from_interface(i_iteration) diff --git a/guardrails/classes/history/outputs.py b/guardrails/classes/history/outputs.py index ead39de20..dda4c7340 100644 --- a/guardrails/classes/history/outputs.py +++ b/guardrails/classes/history/outputs.py @@ -1,39 +1,47 @@ -from typing import Dict, List, Optional, Sequence, Union +from typing import Any, Dict, List, Optional, Union from pydantic import Field -from typing_extensions import deprecated +from guardrails_api_client import ( + Outputs as IOutputs, + OutputsParsedOutput, + OutputsValidationResponse, +) from guardrails.constants import error_status, fail_status, not_run_status, pass_status -from guardrails.utils.llm_response import LLMResponse -from guardrails.utils.logs_utils import ValidatorLogs -from guardrails.utils.pydantic_utils import ArbitraryModel -from guardrails.utils.reask_utils import ReAsk -from guardrails.validator_base import ErrorSpan, FailResult, ValidationResult +from guardrails.classes.llm.llm_response import LLMResponse +from guardrails.classes.generic.arbitrary_model import ArbitraryModel +from guardrails.classes.validation.validator_logs import ValidatorLogs +from guardrails.actions.reask import ReAsk +from guardrails.classes.validation.validation_result import ( + ErrorSpan, + FailResult, + ValidationResult, +) -class Outputs(ArbitraryModel): +class Outputs(IOutputs, ArbitraryModel): llm_response_info: Optional[LLMResponse] = Field( description="Information from the LLM response.", default=None ) raw_output: Optional[str] = Field( description="The exact output from the LLM.", default=None ) - parsed_output: Optional[Union[str, Dict]] = Field( + parsed_output: Optional[Union[str, List, Dict]] = Field( description="The output parsed from the LLM response" "as it was passed into validation.", default=None, ) - validation_response: Optional[Union[str, ReAsk, Dict]] = Field( + validation_response: Optional[Union[str, ReAsk, List, Dict]] = Field( description="The response from the validation process.", default=None ) - guarded_output: Optional[Union[str, Dict]] = Field( + guarded_output: Optional[Union[str, List, Dict]] = Field( description="""Any valid values after undergoing validation. Some values may be "fixed" values that were corrected during validation. This property may be a partial structure if field level reasks occur.""", default=None, ) - reasks: Sequence[ReAsk] = Field( + reasks: List[ReAsk] = Field( description="Information from the validation process" "used to construct a ReAsk to the LLM on validation failure.", default_factory=list, @@ -82,23 +90,31 @@ def error_spans_in_output(self) -> List[ErrorSpan]: These indices are relative to the complete LLM output. """ - total_len = 0 + # map of total length to validator + total_len_by_validator = {} spans_in_output = [] for log in self.validator_logs: + validator_name = log.validator_name + if total_len_by_validator.get(validator_name) is None: + total_len_by_validator[validator_name] = 0 result = log.validation_result if isinstance(result, FailResult): if result.error_spans is not None: for error_span in result.error_spans: spans_in_output.append( ErrorSpan( - start=error_span.start + total_len, - end=error_span.end + total_len, + start=error_span.start + + total_len_by_validator[validator_name], + end=error_span.end + + total_len_by_validator[validator_name], reason=error_span.reason, ) ) if isinstance(result, ValidationResult): if result and result.validated_chunk is not None: - total_len += len(result.validated_chunk) + total_len_by_validator[validator_name] += len( + result.validated_chunk + ) return spans_in_output @property @@ -127,18 +143,80 @@ def status(self) -> str: return fail_status return pass_status - @property - @deprecated( - """'Outputs.validation_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'validation_response' instead.""" - ) - def validation_output(self) -> Optional[Union[str, ReAsk, Dict]]: - return self.validation_response + def to_interface(self) -> IOutputs: + return IOutputs( + llm_response_info=( # type: ignore - pydantic alias + self.llm_response_info.to_interface() + if self.llm_response_info + else None + ), + raw_output=self.raw_output, # type: ignore - pydantic alias + parsed_output=( # type: ignore - pydantic alias + OutputsParsedOutput(self.parsed_output) if self.parsed_output else None + ), + validation_response=( # type: ignore - pydantic alias + OutputsValidationResponse(self.validation_response) + if self.validation_response + else None + ), + guarded_output=( # type: ignore - pydantic alias + OutputsParsedOutput(self.guarded_output) + if self.guarded_output + else None + ), + reasks=self.reasks, # type: ignore - pydantic alias + validator_logs=[ # type: ignore - pydantic alias + v.to_interface() + for v in self.validator_logs + if isinstance(v, ValidatorLogs) + ], + error=self.error, + ) - @property - @deprecated( - """'Outputs.validated_output' is deprecated and will be removed in \ -versions 0.5.0 and beyond. Use 'guarded_output' instead.""" - ) - def validated_output(self) -> Optional[Union[str, ReAsk, Dict]]: - return self.guarded_output + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_outputs: IOutputs) -> "Outputs": + reasks = [] + if i_outputs.reasks: + reasks = [ReAsk.from_interface(r) for r in i_outputs.reasks] + + validator_logs = [] + if i_outputs.validator_logs: + validator_logs = [ + ValidatorLogs.from_interface(v) for v in i_outputs.validator_logs + ] + + return cls( + llm_response_info=( # type: ignore + LLMResponse.from_interface(i_outputs.llm_response_info) + if i_outputs.llm_response_info + else None + ), + raw_output=i_outputs.raw_output, # type: ignore + parsed_output=( # type: ignore + i_outputs.parsed_output.actual_instance + if i_outputs.parsed_output + else None + ), + validation_response=( # type: ignore + i_outputs.validation_response.actual_instance + if i_outputs.validation_response + else None + ), + guarded_output=( # type: ignore + i_outputs.guarded_output.actual_instance + if i_outputs.guarded_output + else None + ), + reasks=reasks, # type: ignore + validator_logs=validator_logs, # type: ignore + error=i_outputs.error, + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "Outputs": + i_outputs = IOutputs.from_dict(obj) or IOutputs() + + return cls.from_interface(i_outputs) diff --git a/guardrails/classes/llm/llm_response.py b/guardrails/classes/llm/llm_response.py new file mode 100644 index 000000000..6051d2fca --- /dev/null +++ b/guardrails/classes/llm/llm_response.py @@ -0,0 +1,72 @@ +import asyncio +from typing import Any, Dict, Iterable, Optional, AsyncIterable + +from guardrails_api_client import LLMResponse as ILLMResponse +from pydantic.config import ConfigDict + + +# TODO: Move this somewhere that makes sense +def async_to_sync(awaitable): + loop = asyncio.get_event_loop() + return loop.run_until_complete(awaitable) + + +# TODO: We might be able to delete this +class LLMResponse(ILLMResponse): + # Pydantic Config + model_config = ConfigDict(arbitrary_types_allowed=True) + + prompt_token_count: Optional[int] = None + response_token_count: Optional[int] = None + output: str + stream_output: Optional[Iterable] = None + async_stream_output: Optional[AsyncIterable] = None + + def to_interface(self) -> ILLMResponse: + stream_output = None + if self.stream_output: + stream_output = [str(so) for so in self.stream_output] + + async_stream_output = None + if self.async_stream_output: + async_stream_output = [str(async_to_sync(so)) for so in self.stream_output] # type: ignore - we just established it isn't None + + return ILLMResponse( + prompt_token_count=self.prompt_token_count, # type: ignore - pyright doesn't understand aliases + response_token_count=self.response_token_count, # type: ignore - pyright doesn't understand aliases + output=self.output, + stream_output=stream_output, # type: ignore - pyright doesn't understand aliases + async_stream_output=async_stream_output, # type: ignore - pyright doesn't understand aliases + ) + + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_llm_response: ILLMResponse) -> "LLMResponse": + stream_output = None + if i_llm_response.stream_output: + stream_output = [so for so in i_llm_response.stream_output] + + async_stream_output = None + if i_llm_response.async_stream_output: + + async def async_iter(): + for aso in i_llm_response.async_stream_output: # type: ignore - just verified it isn't None... + yield aso + + async_stream_output = async_iter() + + return cls( + prompt_token_count=i_llm_response.prompt_token_count, + response_token_count=i_llm_response.response_token_count, + output=i_llm_response.output, + stream_output=stream_output, + async_stream_output=async_stream_output, + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "LLMResponse": + i_llm_response = super().from_dict(obj) or ILLMResponse(output="") + + return cls.from_interface(i_llm_response) diff --git a/guardrails/classes/output_type.py b/guardrails/classes/output_type.py index 2aff9fc82..e1b050921 100644 --- a/guardrails/classes/output_type.py +++ b/guardrails/classes/output_type.py @@ -1,3 +1,63 @@ -from typing import Dict, List, TypeVar +# TODO: Move this file to guardrails.types +from enum import Enum +from typing import Any, Dict, List, Optional, TypeVar, Union +from guardrails_api_client import SimpleTypes OT = TypeVar("OT", str, List, Dict) + + +# TODO: Move this to types.py +# It's only here for historical reasons +class OutputTypes(str, Enum): + STRING = "str" + LIST = "list" + DICT = "dict" + + @staticmethod + def get(key: Optional[Union[str, "OutputTypes"]], default=None): + try: + if not key: + return default + if isinstance(key, OutputTypes): + return key + return OutputTypes[key] + except Exception: + return default + + @classmethod + def __from_json_schema__(cls, json_schema: Dict[str, Any]) -> "OutputTypes": + if not json_schema: + return cls("str") + + schema_type = json_schema.get("type") + if schema_type == SimpleTypes.STRING: + return cls("str") + elif schema_type == SimpleTypes.OBJECT: + return cls("dict") + elif schema_type == SimpleTypes.ARRAY: + return cls("list") + + all_of = json_schema.get("allOf") + if all_of: + return cls("dict") + + one_of: List[Dict[str, Any]] = [ + s + for s in json_schema.get("oneOf", []) + if isinstance(s, dict) and "type" in s + ] + if one_of: + first_sub_schema = one_of[0] + return cls.__from_json_schema__(first_sub_schema) + + any_of: List[Dict[str, Any]] = [ + s + for s in json_schema.get("anyOf", []) + if isinstance(s, dict) and "type" in s + ] + if any_of: + first_sub_schema = any_of[0] + return cls.__from_json_schema__(first_sub_schema) + + # Fallback to string + return cls("str") diff --git a/guardrails/classes/schema/__init__.py b/guardrails/classes/schema/__init__.py new file mode 100644 index 000000000..b85bd10f1 --- /dev/null +++ b/guardrails/classes/schema/__init__.py @@ -0,0 +1,3 @@ +from guardrails.classes.schema.processed_schema import ProcessedSchema + +__all__ = ["ProcessedSchema"] diff --git a/guardrails/classes/schema/model_schema.py b/guardrails/classes/schema/model_schema.py new file mode 100644 index 000000000..cb28770ee --- /dev/null +++ b/guardrails/classes/schema/model_schema.py @@ -0,0 +1,28 @@ +from typing import Any, Dict, Optional +from guardrails_api_client import ModelSchema as IModelSchema, ValidationType + + +# Because pydantic insists on including None values in the serialized dictionary +class ModelSchema(IModelSchema): + def to_dict(self) -> Dict[str, Any]: + super_dict = super().to_dict() + return {k: v for k, v in super_dict.items() if v is not None} + + @classmethod + def from_dict(cls, obj: Optional[Dict[str, Any]]) -> "ModelSchema": + if not obj: + obj = {"type": "string"} + + i_model_schema = super().from_dict(obj) + + i_model_schema_dict = ( + i_model_schema.to_dict() if i_model_schema else {"type": "string"} + ) + + trimmed = {k: v for k, v in i_model_schema_dict.items() if v is not None} + + output_schema_type = trimmed.get("type") + if output_schema_type: + trimmed["type"] = ValidationType.from_dict(output_schema_type) # type: ignore + + return cls(**trimmed) # type: ignore diff --git a/guardrails/classes/schema/processed_schema.py b/guardrails/classes/schema/processed_schema.py new file mode 100644 index 000000000..5e23544d0 --- /dev/null +++ b/guardrails/classes/schema/processed_schema.py @@ -0,0 +1,19 @@ +from dataclasses import dataclass, field +from typing import Any, Dict, List +from guardrails_api_client import ValidatorReference +from guardrails.classes.execution.guard_execution_options import GuardExecutionOptions +from guardrails.classes.output_type import OutputTypes +from guardrails.types.validator import ValidatorMap + + +@dataclass +class ProcessedSchema: + """This class is just a container for the various pieces of information we + extract from the various schema wrappers a user can pass in; i.e. RAIL or + Pydantic.""" + + output_type: OutputTypes = field(default=OutputTypes.STRING) + validators: List[ValidatorReference] = field(default_factory=list) + validator_map: ValidatorMap = field(default_factory=dict) + json_schema: Dict[str, Any] = field(default_factory=dict) + exec_opts: GuardExecutionOptions = field(default_factory=GuardExecutionOptions) diff --git a/guardrails/classes/templating/constants_container.py b/guardrails/classes/templating/constants_container.py new file mode 100644 index 000000000..2996480fe --- /dev/null +++ b/guardrails/classes/templating/constants_container.py @@ -0,0 +1,64 @@ +import os +from lxml import etree as ET + + +class ConstantsContainer: + def __init__(self): + self._constants = {} + self.fill_constants() + + def fill_constants(self) -> None: + self_file_path = os.path.dirname(__file__) + self_dirname = os.path.dirname(self_file_path) + constants_file = os.path.abspath( + os.path.join(self_dirname, "..", "constants.xml") + ) + + with open(constants_file, "r") as f: + xml = f.read() + + parser = ET.XMLParser(encoding="utf-8") + parsed_constants = ET.fromstring(xml, parser=parser) + + for child in parsed_constants: + if isinstance(child, ET._Comment): + continue + if isinstance(child, str): + continue + + constant_name = child.tag + constant_value = child.text + self._constants[constant_name] = constant_value + + def __getitem__(self, key): + return self._constants[key] + + def __setitem__(self, key, value): + self._constants[key] = value + + def __delitem__(self, key): + del self._constants[key] + + def __iter__(self): + return iter(self._constants) + + def __len__(self): + return len(self._constants) + + def __contains__(self, key): + return key in self._constants + + def __repr__(self): + return repr(self._constants) + + def __str__(self): + return str(self._constants) + + def items(self): + return self._constants.items() + + def keys(self): + return self._constants.keys() + + def values(self): + return self._constants.values() diff --git a/guardrails/namespace_template.py b/guardrails/classes/templating/namespace_template.py similarity index 100% rename from guardrails/namespace_template.py rename to guardrails/classes/templating/namespace_template.py diff --git a/guardrails/classes/validation/validation_result.py b/guardrails/classes/validation/validation_result.py new file mode 100644 index 000000000..316f3c415 --- /dev/null +++ b/guardrails/classes/validation/validation_result.py @@ -0,0 +1,139 @@ +from typing import Any, Dict, List, Literal, Optional, Union +from pydantic import Field +from guardrails_api_client import ( + ValidationResult as IValidationResult, # noqa + PassResult as IPassResult, + FailResult as IFailResult, + ErrorSpan as IErrorSpan, +) +from guardrails.classes.generic.arbitrary_model import ArbitraryModel + + +class ValidationResult(IValidationResult, ArbitraryModel): + outcome: str + metadata: Optional[Dict[str, Any]] = None + + # value argument passed to validator.validate + # or validator.validate_stream + validated_chunk: Optional[Any] = None + + @classmethod + def from_interface( + cls, i_validation_result: Union[IValidationResult, IPassResult, IFailResult] + ) -> "ValidationResult": + if i_validation_result.outcome == "pass": + return PassResult( + outcome=i_validation_result.outcome, + metadata=i_validation_result.metadata, + validated_chunk=i_validation_result.validated_chunk, + ) + elif i_validation_result.outcome == "fail": + return FailResult.from_dict(i_validation_result.to_dict()) + + return cls( + outcome=i_validation_result.outcome or "", + metadata=i_validation_result.metadata, + validated_chunk=i_validation_result.validated_chunk, + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "ValidationResult": + i_validation_result = IValidationResult.from_dict(obj) or IValidationResult( + outcome="pail" + ) + return cls.from_interface(i_validation_result) + + +class PassResult(ValidationResult, IPassResult): + outcome: Literal["pass"] = "pass" + + class ValueOverrideSentinel: + pass + + # should only be used if Validator.override_value_on_pass is True + value_override: Optional[Any] = Field(default=ValueOverrideSentinel) + + def to_interface(self) -> IPassResult: + i_pass_result = IPassResult(outcome=self.outcome, metadata=self.metadata) + + if self.value_override is not self.ValueOverrideSentinel: + i_pass_result.value_override = self.value_override + + return i_pass_result + + def to_dict(self) -> Dict[str, Any]: + # Pydantic's model_dump method isn't working properly + _dict = { + "outcome": self.outcome, + "metadata": self.metadata, + "validatedChunk": self.validated_chunk, + "valueOverride": ( + self.value_override + if self.value_override is not self.ValueOverrideSentinel + else None + ), + } + return _dict + + +# FIXME: Add this to json schema +class ErrorSpan(IErrorSpan, ArbitraryModel): + start: int + end: int + # reason validation failed, specific to this chunk + reason: str + + +class FailResult(ValidationResult, IFailResult): + outcome: Literal["fail"] = "fail" + + error_message: str + fix_value: Optional[Any] = None + # segments that caused validation to fail + error_spans: Optional[List[ErrorSpan]] = None + + @classmethod + def from_interface(cls, i_fail_result: IFailResult) -> "FailResult": + error_spans = None + if i_fail_result.error_spans: + error_spans = [ + ErrorSpan( + start=error_span.start, + end=error_span.end, + reason=error_span.reason, + ) + for error_span in i_fail_result.error_spans + ] + + return cls( + outcome="fail", + metadata=i_fail_result.metadata, + validated_chunk=i_fail_result.validated_chunk, + error_message=i_fail_result.error_message or "", + fix_value=i_fail_result.fix_value, + error_spans=error_spans, + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "FailResult": + i_fail_result = IFailResult.from_dict(obj) or IFailResult( + outcome="Fail", + error_message="", # type: ignore - pyright doesn't understand aliases + ) + return cls.from_interface(i_fail_result) + + def to_dict(self) -> Dict[str, Any]: + # Pydantic's model_dump method isn't working properly + _dict = { + "outcome": self.outcome, + "metadata": self.metadata, + "validatedChunk": self.validated_chunk, + "errorMessage": self.error_message, + "fixValue": self.fix_value, + "errorSpans": ( + [error_span.to_dict() for error_span in self.error_spans] + if self.error_spans + else [] + ), + } + return _dict diff --git a/guardrails/classes/validation/validator_logs.py b/guardrails/classes/validation/validator_logs.py new file mode 100644 index 000000000..7cf31f193 --- /dev/null +++ b/guardrails/classes/validation/validator_logs.py @@ -0,0 +1,86 @@ +from datetime import datetime +from typing import Any, Dict, Optional + +from guardrails_api_client import ( + ValidatorLog as IValidatorLog, + ValidatorLogInstanceId, + ValidatorLogValidationResult, +) +from guardrails.classes.generic.arbitrary_model import ArbitraryModel +from guardrails.classes.validation.validation_result import ValidationResult +from guardrails.utils.casting_utils import to_int + + +class ValidatorLogs(IValidatorLog, ArbitraryModel): + """Logs for a single validator.""" + + validator_name: str + registered_name: str + value_before_validation: Any + validation_result: Optional[ValidationResult] = None + value_after_validation: Optional[Any] = None + start_time: Optional[datetime] = None + end_time: Optional[datetime] = None + instance_id: Optional[int] = None + property_path: str + + def to_interface(self) -> IValidatorLog: + start_time = self.start_time.isoformat() if self.start_time else None + end_time = self.end_time.isoformat() if self.end_time else None + return IValidatorLog( + validator_name=self.validator_name, # type: ignore - pyright doesn't understand aliases + registered_name=self.registered_name, # type: ignore - pyright doesn't understand aliases + instance_id=ValidatorLogInstanceId(self.instance_id), # type: ignore - pyright doesn't understand aliases + property_path=self.property_path, # type: ignore - pyright doesn't understand aliases + value_before_validation=self.value_before_validation, # type: ignore - pyright doesn't understand aliases + value_after_validation=self.value_after_validation, # type: ignore - pyright doesn't understand aliases + validation_result=ValidatorLogValidationResult(self.validation_result), # type: ignore - pyright doesn't understand aliases + start_time=start_time, # type: ignore - pyright doesn't understand aliases + end_time=end_time, # type: ignore - pyright doesn't understand aliases + ) + + def to_dict(self) -> Dict[str, Any]: + return self.to_interface().to_dict() + + @classmethod + def from_interface(cls, i_validator_log: IValidatorLog) -> "ValidatorLogs": + instance_id = ( + i_validator_log.instance_id.actual_instance + if i_validator_log.instance_id + else None + ) + validation_result = ( + ValidationResult.from_interface( + i_validator_log.validation_result.actual_instance + ) + if ( + i_validator_log.validation_result + and i_validator_log.validation_result.actual_instance + ) + else None + ) + + start_time = i_validator_log.start_time + if isinstance(start_time, str): + start_time = datetime.fromisoformat(start_time) + + end_time = i_validator_log.end_time + if isinstance(end_time, str): + end_time = datetime.fromisoformat(end_time) + + return cls( + validator_name=i_validator_log.validator_name, + registered_name=i_validator_log.registered_name, + instance_id=to_int(instance_id) or 0, + property_path=i_validator_log.property_path, + value_before_validation=i_validator_log.value_before_validation, + value_after_validation=i_validator_log.value_after_validation, + validation_result=validation_result, + start_time=start_time, + end_time=end_time, + ) + + @classmethod + def from_dict(cls, obj: Dict[str, Any]) -> "ValidatorLogs": + i_validator_log = IValidatorLog.from_dict(obj) + return cls.from_interface(i_validator_log) # type: ignore diff --git a/guardrails/classes/validation_outcome.py b/guardrails/classes/validation_outcome.py index c59eadf2d..fe23a1e91 100644 --- a/guardrails/classes/validation_outcome.py +++ b/guardrails/classes/validation_outcome.py @@ -3,14 +3,19 @@ from pydantic import Field from rich.pretty import pretty_repr +from guardrails_api_client import ( + ValidationOutcome as IValidationOutcome, + ValidationOutcomeValidatedOutput, +) +from guardrails.actions.reask import ReAsk from guardrails.classes.history import Call, Iteration from guardrails.classes.output_type import OT +from guardrails.classes.generic.arbitrary_model import ArbitraryModel from guardrails.constants import pass_status -from guardrails.utils.logs_utils import ArbitraryModel -from guardrails.utils.reask_utils import ReAsk +from guardrails.utils.safe_get import safe_get -class ValidationOutcome(ArbitraryModel, Generic[OT]): +class ValidationOutcome(IValidationOutcome, ArbitraryModel, Generic[OT]): raw_llm_output: Optional[str] = Field( description="The raw, unchanged output from the LLM call.", default=None ) @@ -50,13 +55,16 @@ class ValidationOutcome(ArbitraryModel, Generic[OT]): @classmethod def from_guard_history(cls, call: Call): """Create a ValidationOutcome from a history Call object.""" - last_iteration = call.iterations.last or Iteration() - last_output = last_iteration.validation_response or last_iteration.parsed_output + last_iteration = call.iterations.last or Iteration(call_id=call.id, index=0) + last_output = last_iteration.validation_response or safe_get( + list(last_iteration.reasks), 0 + ) validation_passed = call.status == pass_status reask = last_output if isinstance(last_output, ReAsk) else None error = call.error output = cast(OT, call.guarded_output) return cls( + call_id=call.id, # type: ignore raw_llm_output=call.raw_outputs.last, validated_output=output, reask=reask, @@ -87,3 +95,15 @@ def __getitem__(self, keys): def __str__(self) -> str: return pretty_repr(self) + + def to_dict(self): + i_validation_outcome = IValidationOutcome( + call_id=self.call_id, # type: ignore + raw_llm_output=self.raw_llm_output, # type: ignore + validated_output=ValidationOutcomeValidatedOutput(self.validated_output), # type: ignore + reask=self.reask, + validation_passed=self.validation_passed, # type: ignore + error=self.error, + ) + + return i_validation_outcome.to_dict() diff --git a/guardrails/cli/__init__.py b/guardrails/cli/__init__.py index f1b21618b..73bc8bfe9 100644 --- a/guardrails/cli/__init__.py +++ b/guardrails/cli/__init__.py @@ -1,7 +1,11 @@ import guardrails.cli.configure # noqa +import guardrails.cli.start # noqa import guardrails.cli.validate # noqa +from guardrails.cli.create import create_command # noqa: F401 from guardrails.cli.guardrails import guardrails as cli from guardrails.cli.hub import hub_command +from guardrails.cli.watch import watch_command # noqa: F401 + cli.add_typer( hub_command, name="hub", help="Manage validators installed from the Guardrails Hub." diff --git a/guardrails/cli/configure.py b/guardrails/cli/configure.py index 2a7354892..b56fcf43f 100644 --- a/guardrails/cli/configure.py +++ b/guardrails/cli/configure.py @@ -4,25 +4,31 @@ from os.path import expanduser from typing import Optional -from guardrails.classes.credentials import Credentials -from guardrails.cli.server.hub_client import AuthenticationError, get_auth import typer +from guardrails.classes.credentials import Credentials from guardrails.cli.guardrails import guardrails from guardrails.cli.logger import LEVELS, logger +from guardrails.cli.hub.console import console +from guardrails.cli.server.hub_client import AuthenticationError, get_auth DEFAULT_TOKEN = "" DEFAULT_ENABLE_METRICS = True +DEFAULT_USE_REMOTE_INFERENCING = True def save_configuration_file( - token: Optional[str], enable_metrics: Optional[bool] + token: Optional[str], + enable_metrics: Optional[bool], + use_remote_inferencing: Optional[bool] = DEFAULT_USE_REMOTE_INFERENCING, ) -> None: if token is None: token = DEFAULT_TOKEN if enable_metrics is None: enable_metrics = DEFAULT_ENABLE_METRICS + if use_remote_inferencing is None: + use_remote_inferencing = DEFAULT_USE_REMOTE_INFERENCING home = expanduser("~") guardrails_rc = os.path.join(home, ".guardrailsrc") @@ -30,7 +36,8 @@ def save_configuration_file( lines = [ f"id={str(uuid.uuid4())}{os.linesep}", f"token={token}{os.linesep}", - f"enable_metrics={str(enable_metrics).lower()}", + f"enable_metrics={str(enable_metrics).lower()}{os.linesep}", + f"use_remote_inferencing={str(use_remote_inferencing).lower()}", ] rc_file.writelines(lines) rc_file.close() @@ -46,12 +53,6 @@ def _get_default_token() -> str: @guardrails.command() def configure( - token: Optional[str] = typer.Option( - default_factory=_get_default_token, - help="Your Guardrails Hub auth token.", - hide_input=True, - prompt="Token (optional)", - ), enable_metrics: Optional[bool] = typer.Option( DEFAULT_ENABLE_METRICS, "--enable-metrics/--disable-metrics", @@ -64,10 +65,42 @@ def configure( help="Clear the existing token from the configuration file.", ), ): - if clear_token is True: + existing_token = _get_default_token() + last4 = existing_token[-4:] if existing_token else "" + + if not clear_token: + console.print("\nEnter API Key below", style="bold", end=" ") + + if last4: + console.print( + "[dim]leave empty if you want to keep existing token[/dim]", + style="italic", + end=" ", + ) + console.print(f"[{last4}]", style="italic") + + console.print( + ":backhand_index_pointing_right: You can find your API Key at https://hub.guardrailsai.com/keys" + ) + + token = typer.prompt("\nAPI Key", existing_token, show_default=False) + + else: token = DEFAULT_TOKEN + + # Ask about remote inferencing + use_remote_inferencing = ( + typer.prompt( + "Do you wish to use remote inferencing? (Y/N)", + type=str, + default="Y", + show_default=False, + ).lower() + == "y" + ) + try: - save_configuration_file(token, enable_metrics) + save_configuration_file(token, enable_metrics, use_remote_inferencing) logger.info("Configuration saved.") if not token: @@ -77,7 +110,7 @@ def configure( logger.error(e) sys.exit(1) - # Authenticate with the Hub if token is not empty + # Authenticate with the Hub if token is not empty if token != "" and token is not None: logger.info("Validating credentials...") try: diff --git a/guardrails/cli/create.py b/guardrails/cli/create.py new file mode 100644 index 000000000..1486b1f2c --- /dev/null +++ b/guardrails/cli/create.py @@ -0,0 +1,147 @@ +import os +import sys +import time +from typing import List, Optional, Union + +import typer +from rich.console import Console +from rich.syntax import Syntax + +from guardrails.cli.guardrails import guardrails as gr_cli +from guardrails.cli.hub.install import ( # JC: I don't like this import. Move fns? + install_hub_module, + add_to_hub_inits, + run_post_install, +) +from guardrails.cli.hub.utils import get_site_packages_location +from guardrails.cli.server.hub_client import get_validator_manifest + + +console = Console() + + +@gr_cli.command(name="create") +def create_command( + validators: str = typer.Option( + help="A comma-separated list of validator hub URIs. ", + ), + name: Optional[str] = typer.Option( + default=None, help="The name of the guard to define in the file." + ), + filepath: str = typer.Option( + default="config.py", + help="The path to which the configuration file should be saved.", + ), + dry_run: bool = typer.Option( + default=False, + is_flag=True, + help="Print out the validators to be installed without making any changes.", + ), +): + filepath = check_filename(filepath) + installed_validators = split_and_install_validators(validators, dry_run) + new_config_file = generate_config_file(installed_validators, name) + if dry_run: + console.print(f"Not actually saving output to [bold]{filepath}[/bold]") + console.print("The following would have been written:\n") + formatted = Syntax(new_config_file, "python") + console.print(formatted) + console.print("\n") + else: + with open(filepath, "wt") as fout: + fout.write(new_config_file) + console.print(f"Saved configuration to {filepath}") + console.print( + f"Replace TODOs in {filepath} and run with `guardrails start" + f" --config {filepath}`" + ) + + +def check_filename(filename: Union[str, os.PathLike]) -> str: + """If a filename is specified and already exists, will prompt the user to confirm + overwriting. Aborts if the user declines.""" + if os.path.exists(filename): + # Alert the user and get confirmation of overwrite. + overwrite = typer.confirm( + f"The configuration file {filename} already exists. Overwrite?" + ) + if not overwrite: + console.print("Aborting") + typer.Abort() + sys.exit(0) # Force exit if we fall through. + return filename # type: ignore + + +def split_and_install_validators(validators: str, dry_run: bool = False): + """Given a comma-separated list of validators, check the hub to make sure all of + them exist, install them, and return a list of 'imports'.""" + stripped_validators = list() + manifests = list() + site_packages = get_site_packages_location() + + # Split by comma, strip start and end spaces, then make sure there's a hub prefix. + # If all that passes, download the manifest file so we know where to install. + # hub://blah -> blah, then download the manifest. + console.print("Checking validators...") + with console.status("Checking validator manifests") as status: + for v in validators.split(","): + status.update(f"Prefetching {v}") + if not v.strip().startswith("hub://"): + console.print( + f"WARNING: Validator {v} does not appear to be a valid URI." + ) + sys.exit(-1) + stripped_validator = v.lstrip("hub://") + stripped_validators.append(stripped_validator) + manifests.append(get_validator_manifest(stripped_validator)) + console.print("Success!") + + # We should make sure they exist. + console.print("Installing...") + with console.status("Installing validators") as status: + for manifest, validator in zip(manifests, stripped_validators): + status.update(f"Installing {validator}") + if not dry_run: + install_hub_module(manifest, site_packages, quiet=True) + run_post_install(manifest, site_packages) + add_to_hub_inits(manifest, site_packages) + else: + console.print(f"Fake installing {validator}") + time.sleep(1) + console.print("Success!") + + # Pull the hub information from each of the installed validators and return it. + return [manifest.exports[0] for manifest in manifests] + + +def generate_config_file(validators: List[str], name: Optional[str] = None) -> str: + console.print("Generating config file...") + config_lines = [ + "from guardrails import Guard", + ] + + # Import one or more validators. + if len(validators) == 1: + config_lines.append(f"from guardrails.hub import {validators[0]}") + else: + multiline_import = ",\n\t".join(validators) + config_lines.append(f"from guardrails.hub import (\n\t{multiline_import}\n)") + + # Initialize our guard. + config_lines.append("guard = Guard()") + if name is not None: + config_lines.append(f"guard.name = {name.__repr__()}") + + # Append validators: + if len(validators) == 1: + config_lines.append(f"guard.use({validators[0]}( TODO Fill these parameters ))") + else: + multi_use = ",\n".join( + [ + "\t" + validator + "( TODO fill these parameters )" + for validator in validators + ] + ) + config_lines.append(f"guard.use_many(\n{multi_use}\n)") + + return "\n".join(config_lines) diff --git a/guardrails/cli/hub/install.py b/guardrails/cli/hub/install.py index 931b2383f..ac6da49a2 100644 --- a/guardrails/cli/hub/install.py +++ b/guardrails/cli/hub/install.py @@ -1,7 +1,7 @@ -from contextlib import contextmanager import os import subprocess import sys +from contextlib import contextmanager from string import Template from typing import List, Literal @@ -9,15 +9,16 @@ from guardrails.classes.generic import Stack from guardrails.cli.hub.hub import hub_command +from guardrails.cli.hub.utils import ( + get_hub_directory, + get_org_and_package_dirs, + get_site_packages_location, + pip_process, +) from guardrails.cli.logger import LEVELS, logger from guardrails.cli.server.hub_client import get_validator_manifest from guardrails.cli.server.module_manifest import ModuleManifest -from guardrails.cli.hub.utils import pip_process -from guardrails.cli.hub.utils import get_site_packages_location -from guardrails.cli.hub.utils import get_org_and_package_dirs -from guardrails.cli.hub.utils import get_hub_directory - from .console import console @@ -84,7 +85,6 @@ def add_to_hub_inits(manifest: ModuleManifest, site_packages: str): def run_post_install(manifest: ModuleManifest, site_packages: str): org_package = get_org_and_package_dirs(manifest) post_install_script = manifest.post_install - if not post_install_script: return @@ -237,11 +237,35 @@ def do_nothing_context(*args, **kwargs): with loader(dl_deps_msg, spinner="bouncingBar"): install_hub_module(module_manifest, site_packages, quiet=quiet) + try: + if module_manifest.tags and module_manifest.tags.has_guardrails_endpoint: + install_local_models = typer.confirm( + "This validator has a Guardrails AI inference endpoint available. " + "Would you still like to install the local models for local inference?", + ) + else: + install_local_models = typer.confirm( + "Would you like to install the local models?", default=True + ) + except AttributeError: + install_local_models = False + # Post-install - post_msg = "Running post-install setup" - with loader(post_msg, spinner="bouncingBar"): - run_post_install(module_manifest, site_packages) - add_to_hub_inits(module_manifest, site_packages) + if install_local_models: + logger.log( + level=LEVELS.get("SPAM"), # type: ignore + msg="Installing models locally!", + ) + post_msg = "Running post-install setup" + with loader(post_msg, spinner="bouncingBar"): + run_post_install(module_manifest, site_packages) + else: + logger.log( + level=LEVELS.get("SPAM"), # type: ignore + msg="Skipping post install, models will not be " + "downloaded for local inference.", + ) + add_to_hub_inits(module_manifest, site_packages) logger.info("Installation complete") diff --git a/guardrails/cli/server/module_manifest.py b/guardrails/cli/server/module_manifest.py index 61a5c7ccf..34e36d820 100644 --- a/guardrails/cli/server/module_manifest.py +++ b/guardrails/cli/server/module_manifest.py @@ -26,6 +26,7 @@ class ModuleTags(Serializeable): content_type: Optional[List[str]] = field(default_factory=list) validation_category: Optional[List[str]] = field(default_factory=list) process_requirements: Optional[List[str]] = field(default_factory=list) + has_guardrails_endpoint: Optional[bool] = field(default_factory=bool) @dataclass diff --git a/guardrails/cli/start.py b/guardrails/cli/start.py new file mode 100644 index 000000000..5d5530abe --- /dev/null +++ b/guardrails/cli/start.py @@ -0,0 +1,41 @@ +from typing import Optional +import typer + +from guardrails.cli.guardrails import guardrails +from guardrails.cli.hub.utils import pip_process +from guardrails.cli.logger import logger + + +def api_is_installed() -> bool: + try: + import guardrails_api # type: ignore # noqa + + return True + except ImportError: + return False + + +@guardrails.command() +def start( + env: Optional[str] = typer.Option( + default="", + help="An env file to load environment variables from.", + ), + config: Optional[str] = typer.Option( + default="", + help="A config file to load Guards from.", + ), + port: Optional[int] = typer.Option( + default=8000, + help="The port to run the server on.", + ), +): + logger.debug("Checking for prerequisites...") + if not api_is_installed(): + package_name = 'guardrails-api>="^0.0.0a0"' + pip_process("install", package_name) + + from guardrails_api.cli.start import start # type: ignore + + logger.info("Starting Guardrails server") + start(env, config, port) diff --git a/guardrails/cli/watch.py b/guardrails/cli/watch.py new file mode 100644 index 000000000..ba163b976 --- /dev/null +++ b/guardrails/cli/watch.py @@ -0,0 +1,60 @@ +import json +import sqlite3 +import time +from dataclasses import asdict +from typing import Optional + +import rich +import typer + +from guardrails.cli.guardrails import guardrails as gr_cli +from guardrails.call_tracing import GuardTraceEntry, TraceHandler + + +@gr_cli.command(name="watch") +def watch_command( + plain: bool = typer.Option( + default=False, + is_flag=True, + help="Do not use any rich formatting, instead printing each entry on a line.", + ), + num_lines: int = typer.Option( + default=0, + help="Print the last n most recent lines. If omitted, will print all history.", + ), + follow: bool = typer.Option( + default=True, + help="Continuously read the last output commands", + ), + log_path_override: Optional[str] = typer.Option( + default=None, help="Specify a path to the log output file." + ), +): + # Open a reader for the log path: + log_reader = None + while log_reader is None: + try: + if log_path_override is not None: + log_reader = TraceHandler.get_reader(log_path_override) # type: ignore + else: + log_reader = TraceHandler.get_reader() + except sqlite3.OperationalError: + print("Logfile not found. Retrying.") + time.sleep(1) + + # If we are using fancy outputs, grab a console ref and prep a table. + output_fn = _print_and_format_plain + if not plain: + output_fn = _print_fancy + + # Spin while tailing, breaking if we aren't continuously tailing. + for log_msg in log_reader.tail_logs(-num_lines, follow): + output_fn(log_msg) + + +def _print_fancy(log_msg: GuardTraceEntry): + rich.print(log_msg) + + +def _print_and_format_plain(log_msg: GuardTraceEntry) -> None: + print(json.dumps(asdict(log_msg))) diff --git a/guardrails/constants.xml b/guardrails/constants.xml index 55d016d4e..7af4906d6 100644 --- a/guardrails/constants.xml +++ b/guardrails/constants.xml @@ -2,24 +2,47 @@ -Return a valid JSON object that respects this XML format and extracts only the information requested in this document. Respect the types indicated in the XML -- the information you extract should be converted into the correct 'type'. Try to be as correct and concise as possible. Find all relevant information in the document. If you are unsure of the answer, enter 'None'. If you answer incorrectly, you will be asked again until you get it right which is expensive. +Return a valid JSON object that respects this JSON Schema and extracts only the information requested in this document. Respect the types indicated in the JSON Schema -- the information you extract should be converted into the correct 'type'. Try to be as correct and concise as possible. Find all relevant information in the document. If you are unsure of the answer, enter 'None'. If you answer incorrectly, you will be asked again until you get it right which is expensive. + +Return a valid JSON object that respects this XML format and extracts only the information requested in this document. Respect the types indicated in the XML -- the information you extract should be converted into the correct 'type'. Try to be as correct and concise as possible. Find all relevant information in the document. If you are unsure of the answer, enter 'None'. If you answer incorrectly, you will be asked again until you get it right which is expensive. + + Given below is XML that describes the information to extract from this document and the tags to extract it into. + +Given below is a JSON Schema that describes the output structure you should return. + + -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 "None". +ONLY return a valid JSON object (no other text is necessary), where the key of the field in the JSON is the key of the entries within the schema's `properties`, and the value is of the type specified by the `type` property under that key. +The JSON MUST conform to the structure described by the JSON Schema provided BUT SHOULD NOT BE A JSON Schema ITSELF. +Be sure to include any types and format requests e.g. requests for lists, objects and specific types. +Be correct and concise. +If you are unsure anywhere, enter "None". + +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 "None". + + -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. +ONLY return a valid JSON object (no other text is necessary), where the key of the field in the JSON is the key of the entries within the schema's `properties`, and the value is of the type specified by the `type` property under that key. +The JSON MUST conform to the structure described by the JSON Schema provided BUT SHOULD NOT BE A JSON Schema ITSELF. +Be sure to include any types and format requests e.g. requests for lists, objects and specific types. +Be correct and concise. + +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. + + I was given the following JSON response, which had problems due to incorrect values. @@ -45,68 +68,146 @@ Help me correct this by making it valid JSON. -Given below is XML that describes the information to extract from this document and the tags to extract it into. +Given below is a JSON Schema that describes the output structure you should return. ${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`. +ONLY return a valid JSON object (no other text is necessary), where the key of the field in the JSON is the key of the entries within the schema's `properties`, and the value is of the type specified by the `type` property under that key. +The JSON MUST conform to the structure described by the JSON Schema provided BUT SHOULD NOT BE A JSON Schema ITSELF. +Be sure to include 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`. + +Given below is XML that describes the information to extract from this document and the tags to extract it into. + +${xml_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`. + + ${gr.json_suffix_without_examples} Here's an example of the structure: ${json_example} + +${gr.xml_suffix_without_examples} +Here's an example of the structure: +${json_example} + + -Given below is XML that describes the information to extract from this document and the tags to extract it into. +Given below is a JSON Schema that describes the information to extract from this document and the tags to extract it into. ${output_schema} +ONLY return a valid JSON object (no other text is necessary), where the key of the field in the JSON is the key of the entries within the schema's `properties`, and the value is of the type specified by the `type` property under that key. +The JSON MUST conform to the structure described by the JSON Schema provided BUT SHOULD NOT BE A JSON Schema ITSELF. +Be sure to include 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 (JSON Schema, JSON) pairs that show the expected behavior: +- `{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}` => `{'foo': 'example one'}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}}}}` => `{"bar": ['STRING ONE', 'STRING TWO']}` +- `{"type":"object","properties":{"baz":{"type":"object","properties":{"foo":{"type":"string","format":"capitalize two-words"},"index":{"type":"integer","format":"1-indexed"}}}}}` => `{'baz': {'foo': 'Some String', 'index': 1}}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}},"baz":{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}}}` => `{'bar': ['STRING ONE', 'STRING TWO'], 'baz': {'foo': 'example one'}}` + + + +Given below is XML that describes the information to extract from this document and the tags to extract it into. + +${xml_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}}` - + -Given below is XML that describes the information to extract from this document and the tags to extract it into. +Given below is a JSON Schema that describes the information to extract from this document and the tags to extract it into. ${output_schema} +ONLY return a valid JSON object (no other text is necessary). The JSON MUST conform to the JSON Schema, including any types and format requests e.g. requests for lists, objects and specific types. Be correct and concise. + +Here are examples of simple (JSON Schema, JSON) pairs that show the expected behavior: +- `{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}` => `{'foo': 'example one'}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}}}}` => `{"bar": ['STRING ONE', 'STRING TWO']}` +- `{"type":"object","properties":{"baz":{"type":"object","properties":{"foo":{"type":"string","format":"capitalize two-words"},"index":{"type":"integer","format":"1-indexed"}}}}}` => `{'baz': {'foo': 'Some String', 'index': 1}}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}},"baz":{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}}}` => `{'bar': ['STRING ONE', 'STRING TWO'], 'baz': {'foo': 'example one'}}` + + + +Given below is XML that describes the information to extract from this document and the tags to extract it into. + +${xml_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}}` - + -Given below is XML that describes the information to extract from this document and the tags to extract it into. +Given below is JSON Schema that describes the information to extract from this document and the tags to extract it into. ${output_schema} +ONLY return a valid JSON object (no other text is necessary). The JSON MUST conform to the JSON Schema, 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 (JSON Schema, JSON) pairs that show the expected behavior: +- `{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}` => `{'foo': 'example one'}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}}}}` => `{"bar": ['STRING ONE', 'STRING TWO']}` +- `{"type":"object","properties":{"baz":{"type":"object","properties":{"foo":{"type":"string","format":"capitalize two-words"},"index":{"type":"integer","format":"1-indexed"}}}}}` => `{'baz': {'foo': 'Some String', 'index': 1}}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}},"baz":{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}}}` => `{'bar': ['STRING ONE', 'STRING TWO'], 'baz': {'foo': 'example one'}}` + + + +Given below is XML that describes the information to extract from this document and the tags to extract it into. + +${xml_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}}` - + +ONLY return a valid JSON object (no other text is necessary), where the key of the field in the JSON is the key of the entries within the schema's `properties`, and the value is of the type specified by the `type` property under that key. +The JSON MUST conform to the structure described by the JSON Schema provided BUT SHOULD NOT BE A JSON Schema ITSELF. +Be sure to include 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 (JSON Schema, JSON) pairs that show the expected behavior: +- `{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}` => `{'foo': 'example one'}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}}}}` => `{"bar": ['STRING ONE', 'STRING TWO']}` +- `{"type":"object","properties":{"baz":{"type":"object","properties":{"foo":{"type":"string","format":"capitalize two-words"},"index":{"type":"integer","format":"1-indexed"}}}}}` => `{'baz': {'foo': 'Some String', 'index': 1}}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}},"baz":{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}}}` => `{'bar': ['STRING ONE', 'STRING TWO'], 'baz': {'foo': 'example one'}}` + + + 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}}` - + This was a previous response you generated: @@ -125,13 +226,32 @@ ${output_schema} You are a helpful assistant only capable of communicating with valid JSON, and no other text. +ONLY return a valid JSON object (no other text is necessary). The JSON MUST conform to the JSON Schema provided, 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 (JSON Schema, JSON) pairs that show the expected behavior: +- `{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}` => `{'foo': 'example one'}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}}}}` => `{"bar": ['STRING ONE', 'STRING TWO']}` +- `{"type":"object","properties":{"baz":{"type":"object","properties":{"foo":{"type":"string","format":"capitalize two-words"},"index":{"type":"integer","format":"1-indexed"}}}}}` => `{'baz': {'foo': 'Some String', 'index': 1}}` +- `{"type":"object","properties":{"bar":{"type":"array","items":{"type":"string","format":"upper-case"}},"baz":{"type":"object","properties":{"foo":{"type":"string","format":"two-words lower-case"}}}}}` => `{'bar': ['STRING ONE', 'STRING TWO'], 'baz': {'foo': 'example one'}}` + + + +You are a helpful assistant only capable of communicating with valid JSON, and no other text. + 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}}` - + + + + +Error Messages: +${error_messages} + + Return a valid JSON object that respects this XML format and extracts only the information requested in this document. Respect the types indicated in the XML -- the information you extract should be converted into the correct 'type'. Try to be as correct and concise as possible. Find all relevant information in the document. If you are unsure of the answer, enter 'None'. If you answer incorrectly, you will be asked again until you get it right which is expensive. @@ -164,79 +284,4 @@ ${previous_response} Help me correct this by making it valid JSON. - -Given below is XML that describes the information to extract from this document and the tags to extract it into. - -${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`. - - - -${gr.xml_suffix_without_examples} -Here's an example of the structure: -${xml_example} - - - -Given below is XML that describes the information to extract from this document and the tags to extract it into. - -${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}}` - - - -Given below is XML that describes the information to extract from this document and the tags to extract it into. - -${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}}` - - - - -Given below is XML that describes the information to extract from this document and the tags to extract it into. - -${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}}` - - - - -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}}` - - - -You are a helpful assistant only capable of communicating with valid JSON, and no other text. - -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}}` - - \ No newline at end of file diff --git a/guardrails/datatypes.py b/guardrails/datatypes.py index cc117e053..eb80f68d4 100644 --- a/guardrails/datatypes.py +++ b/guardrails/datatypes.py @@ -1,662 +1,10 @@ -import datetime -from dataclasses import dataclass -from types import SimpleNamespace -from typing import Any, Dict, Iterable -from typing import List as TypedList -from typing import Optional, Sequence, Type, TypeVar, Union +from typing import List -from dateutil.parser import parse -from lxml import etree as ET -from typing_extensions import Self +from guardrails_api_client import SimpleTypes +from guardrails.types.rail import RailTypes -from guardrails.utils.casting_utils import to_float, to_int, to_string -from guardrails.utils.xml_utils import cast_xml_to_string -from guardrails.validator_base import Validator, ValidatorSpec -from guardrails.validatorsattr import ValidatorsAttr - -@dataclass -class FieldValidation: - key: Any - value: Any - validators: TypedList[Validator] - children: TypedList["FieldValidation"] - - -def verify_metadata_requirements( - metadata: dict, datatypes: Union["DataType", Iterable["DataType"]] -) -> TypedList[str]: - missing_keys = set() - if isinstance(datatypes, DataType): - datatypes = [datatypes] - 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) - missing_keys = list(missing_keys) - missing_keys.sort() - return missing_keys - - -class DataType: - rail_alias: str - tag: str - - def __init__( - self, - children: Dict[str, Any], - validators_attr: ValidatorsAttr, - optional: bool, - name: Optional[str], - description: Optional[str], - ) -> None: - self._children = children - self.validators_attr = validators_attr - self.name = name - self.description = description - self.optional = optional - - def get_example(self): - raise NotImplementedError - - @property - def validators(self) -> TypedList: - return self.validators_attr.validators - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self._children})" - - def from_str(self, s: str) -> str: - """Create a DataType from a string. - - Note: ScalarTypes like int, float, bool, etc. will override this method. - Other ScalarTypes like string, email, url, etc. will not override this - """ - return s - - 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._constructor_validation(key, value) - - def set_children_from_xml(self, element: ET._Element): - raise NotImplementedError("Abstract method.") - - @classmethod - def from_xml(cls, element: ET._Element, strict: bool = False, **kwargs) -> Self: - # TODO: don't want to pass strict through to DataType, - # but need to pass it to ValidatorsAttr.from_element - # how to handle this? - validators_attr = ValidatorsAttr.from_xml(element, cls.tag, strict) - - is_optional = element.attrib.get("required", "true") == "false" - - name = element.attrib.get("name") - if name is not None: - name = cast_xml_to_string(name) - - description = element.attrib.get("description") - if description is not None: - description = cast_xml_to_string(description) - - data_type = cls({}, validators_attr, is_optional, name, description, **kwargs) - data_type.set_children_from_xml(element) - return data_type - - @property - def children(self) -> SimpleNamespace: - """Return a SimpleNamespace of the children of this DataType.""" - return SimpleNamespace(**self._children) - - def __eq__(self, other): - if not isinstance(other, type(self)): - return False - return self.__dict__ == other.__dict__ - - def _to_request(self) -> Dict[str, Any]: - element: Dict[str, Any] = { - "type": self.tag, - "name": self.name, - "description": self.description, - # This isn't stored anywhere and is inconsistently passed to ValidatorsAttr - # (i.e. is never passed to child properties) - # meaning its purpose isn't consistenly enforced. - # Since this is an XML only property and isn't properly implemented anymore, - # I'm just going to ignore it for now. - "strict": False, - "onFails": [ - {"validatorTag": v.rail_alias, "method": v.on_fail_descriptor} - for v in self.validators_attr.validators - ], - "dateFormat": getattr(self, "date_format", None), - "timeFormat": getattr(self, "time_format", None), - } - formatters = [v.to_xml_attrib() for v in self.validators_attr.validators] - children: Dict[str, Any] = { - k: v._to_request() for k, v in self._children.items() - } - - return {"children": children, "formatters": formatters, "element": element} - - -registry: Dict[str, Type[DataType]] = {} - - -T = TypeVar("T", bound=Type[DataType]) - - -# Create a decorator to register a type -def register_type(name: str): - def decorator(cls: T) -> T: - registry[name] = cls - cls.rail_alias = name - return cls - - return decorator - - -class ScalarType(DataType): - def set_children_from_xml(self, element: ET._Element): - for _ in element: - raise ValueError("ScalarType data type must not have any children.") - - -class NonScalarType(DataType): - pass - - -@register_type("string") -class String(ScalarType): - """Element tag: ``""" - - tag = "string" - - def get_example(self): - return "string" - - def from_str(self, s: str) -> Optional[str]: - """Create a String from a string.""" - return to_string(s) - - @classmethod - def from_string_rail( - cls, - validators: Sequence[ValidatorSpec], - description: Optional[str] = None, - strict: bool = False, - ) -> Self: - return cls( - children={}, - validators_attr=ValidatorsAttr.from_validators(validators, cls.tag, strict), - optional=False, - name=None, - description=description, - ) - - -@register_type("integer") -class Integer(ScalarType): - """Element tag: ``""" - - tag = "integer" - - def get_example(self): - return 1 - - def from_str(self, s: str) -> Optional[int]: - """Create an Integer from a string.""" - return to_int(s) - - -@register_type("float") -class Float(ScalarType): - """Element tag: ``""" - - tag = "float" - - def get_example(self): - return 1.5 - - def from_str(self, s: str) -> Optional[float]: - """Create a Float from a string.""" - return to_float(s) - - -@register_type("bool") -class Boolean(ScalarType): - """Element tag: ``""" - - tag = "bool" - - def get_example(self): - return True - - def from_str(self, s: Union[str, bool]) -> Optional[bool]: - """Create a Boolean from a string.""" - if s is None: - return None - - if isinstance(s, bool): - return s - - if s.lower() == "true": - return True - elif s.lower() == "false": - return False - else: - raise ValueError(f"Invalid boolean value: {s}") - - -@register_type("date") -class Date(ScalarType): - """Element tag: `` - - To configure the date format, create a date-format attribute on the - element. E.g. `` - """ - - tag = "date" - - def __init__( - self, - children: Dict[str, Any], - validators_attr: "ValidatorsAttr", - optional: bool, - name: Optional[str], - description: Optional[str], - ) -> None: - super().__init__(children, validators_attr, optional, name, description) - self.date_format = None - - def get_example(self): - return datetime.date.today() - - def from_str(self, s: str) -> Optional[datetime.date]: - """Create a Date from a string.""" - if s is None: - return None - if not self.date_format: - return parse(s).date() - return datetime.datetime.strptime(s, self.date_format).date() - - @classmethod - def from_xml(cls, element: ET._Element, strict: bool = False) -> "Date": - datatype = super().from_xml(element, strict) - - if "date-format" in element.attrib or "date_format" in element.attrib: - datatype.date_format = element.attrib["date-format"] - - return datatype - - -@register_type("time") -class Time(ScalarType): - """Element tag: `