[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: Auto PR and Merge on Push by Specific User\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  auto-pr-and-merge:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check User\n        id: check_user\n        run: |\n          echo \"user_matched=${{ github.actor == 'unclecode' }}\"\n          echo \"user_matched=${{ github.actor == 'unclecode' }}\" >> $GITHUB_ENV\n\n      - name: Create Pull Request\n        if: env.user_matched == 'true'\n        id: create_pull_request\n        uses: actions/github-script@v5\n        with:\n          script: |\n            const payload = {\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              head: 'main',\n              base: 'live',\n              title: 'Auto PR from main to live',\n              body: 'Automatically generated PR to keep live branch up-to-date',\n              draft: false,\n            };\n            \n            // Create the pull request\n            await github.rest.pulls.create(payload).then(pr => {\n              core.setOutput('pr_number', pr.data.number);\n            }).catch(err => core.setFailed(`Failed to create PR: ${err.message}`));\n\n      - name: Merge Pull Request\n        if: env.user_matched == 'true'\n        uses: actions/github-script@v5\n        with:\n          script: |\n            const pr_number = ${{ steps.create_pull_request.outputs.pr_number }};\n            if (!pr_number) {\n              core.setFailed('PR number is undefined, skipping merge.');\n              return;\n            }\n\n            const payload = {\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              pull_number: parseInt(pr_number, 10),\n              merge_method: 'merge', // Options: 'merge', 'squash', or 'rebase'\n            };\n            \n            // Attempt to merge the pull request\n            await github.rest.pulls.merge(payload).then(response => {\n              if (response.status !== 200) {\n                core.setFailed('Failed to merge the pull request');\n              }\n            }).catch(err => core.setFailed(`Failed to merge PR: ${err.message}`));\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/#use-with-ide\n.pdm.toml\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n\n\napp.log\n.vscode\napp/routes/proxy_all_in_one.py "
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \"control\" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \"submitted\" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of this License; and\nYou must cause any modified files to carry prominent notices stating that You changed the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.\nYou may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "# GroqCall.ai - Lightning-Fast LLM Function Calls\n\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing)\n[![Version](https://img.shields.io/badge/version-0.0.5-blue.svg)](https://github.com/unclecode/groqcall)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nGroqCall is a proxy server that enables lightning-fast function calls for Groq's Language Processing Unit (LPU) and other AI providers. It simplifies the creation of AI assistants by offering a wide range of built-in functions hosted on the cloud.\n\n## Quickstart\n\n### Using the Pre-built Server\n\nTo quickly start using GroqCall without running it locally, make requests to one of the following base URLs:\n\n- Cloud: `https://groqcall.ai/proxy/groq/v1`\n- Local: `http://localhost:8000` (if running the proxy server locally)\n\n### Running the Proxy Locally\n\n1. Clone the repository:\n```\ngit clone https://github.com/unclecode/groqcall.git\ncd groqcall\n```\n\n2. Create and activate a virtual environment:\n```\npython -m venv venv\nsource venv/bin/activate\n```\n\n3. Install dependencies:\n```\npip install -r requirements.txt\n```\n\n4. Run the FastAPI server:\n```\n./venv/bin/uvicorn --app-dir app/ main:app --reload\n```\n\n## Examples\n\n### Using GroqCall with PhiData\n\n```python\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\n\nmy_groq = OpenAILike(\n    model=\"mixtral-8x7b-32768\",\n    api_key=\"YOUR_GROQ_API_KEY\",\n    base_url=\"https://groqcall.ai/proxy/groq/v1\"  # or \"http://localhost:8000/proxy/groq/v1\" if running locally\n)\n\nassistant = Assistant(\n    llm=my_groq,\n    tools=[DuckDuckGo()], \n    show_tool_calls=True, \n    markdown=True\n)\n\nassistant.print_response(\"What's happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n```\n\n### Using GroqCall with Requests\n\n#### FuncHub: Schema-less Function Calls\n\nGroqCall introduces FuncHub, which allows you to make function calls without passing the function schema. \n\n```python\nimport requests\n\napi_key = \"YOUR_GROQ_API_KEY\"\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\" # or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\", \n            \"content\": \"What's happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.search\"\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\"\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n\nprint(response.json()[\"choices\"][0][\"message\"][\"content\"])\n```\n\n- If you notice, the function schema is not passed in the request. This is because GroqCall uses FuncHub to automatically detect and call the function based on the function name in the cloud, Therefore you dont't need to parse the first response, call the function, and pass again. Check \"functions\" folder to add your own functions. I will create more examples in the close future to explain how to add your own functions.\n\n#### Passing Function Schemas\n\nIf you prefer to pass your own function schemas, refer to the [Function Schema example](https://github.com/unclecode/groqcall/blob/main/cookbook/function_call_with_schema.py) in the cookbook.\n\n#### Rune proxy with Ollama locally\n\nFunction call proxy can be used with Ollama. You should first install Ollama and run it locally. Then refer to the [Ollama example](https://github.com/unclecode/groqcall/blob/main/cookbook/function_call_ollama.py) in the cookbook.\n\n## Cookbook\n\nExplore the [Cookbook](https://github.com/unclecode/groqcall/tree/main/cookbook) for more examples and use cases of GroqCall.\n\n## Motivation\n\nGroq is a startup that designs highly specialized processor chips aimed specifically at running inference on large language models. They've introduced what they call the Language Processing Unit (LPU), and the speed is astounding—capable of producing 500 to 800 tokens per second or more.\n\nAs an admirer of Groq and their community, I built this proxy to enable function calls using the OpenAI interface, allowing it to be called from any library. This engineering workaround has proven to be immensely useful in my company for various projects.\n\n## Contributing\n\nContributions are welcome! If you have ideas, suggestions, or would like to contribute to this project, please reach out to me on Twitter (X) @unclecode or via email at unclecode@kidocode.com.\n\nLet's collaborate and make this repository even more awesome! 🚀\n\n## License\n\nThis project is licensed under the Apache License 2.0. See [LICENSE](https://github.com/unclecode/groqcall/blob/main/LICENSE) for more information."
  },
  {
    "path": "README_old.md",
    "content": "# GroqCall.ai (I changed the name from FunckyCall to GroqCall)\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing)\n[![Version](https://img.shields.io/badge/version-0.0.1-blue.svg)](https://github.com/unclecode/groqcall)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nGroqCall is a proxy server provides function call for Groq's lightning-fast Language Processing Unit (LPU) and other AI providers. Additionally, the upcoming FuncyHub will offer a wide range of built-in functions, hosted on the cloud, making it easier to create AI assistants without the need to maintain function schemas in the codebase or or execute them through multiple calls.\n\n## Motivation 🚀\nGroq is a startup that designs highly specialized processor chips aimed specifically at running inference on large language models. They've introduced what they call the Language Processing Unit (LPU), and the speed is astounding—capable of producing 500 to 800 tokens per second or more. I've become a big fan of Groq and their community;\n\n\nI admire what they're doing. It feels like after discovering electricity, the next challenge is moving it around quickly and efficiently. Groq is doing just that for Artificial Intelligence, making it easily accessible everywhere. They've opened up their API to the cloud, but as of now, they lack a function call capability.\n\nUnable to wait for this feature, I built a proxy that enables function calls using the OpenAI interface, allowing it to be called from any library. This engineering workaround has proven to be immensely useful in my company for various projects. Here's the link to the GitHub repository where you can explore and play around with it. I've included some examples in this collaboration for you to check out.\n\n<img width=\"150\" src = \"https://res.cloudinary.com/kidocode/image/upload/v1710148127/GroqChip-1-Die_lgi95d.jpg\"/>\n\n<img title=\"Powered by Groq\" alt=\"Powered by Groq\" width = \"150\" src=\"https://res.cloudinary.com/kidocode/image/upload/v1710142103/Stack_PBG_White_n6qdbj.svg\">\n\n\n\n## Running the Proxy Locally 🖥️\nTo run this proxy locally on your own machine, follow these steps:\n\n1. Clone the GitHub repository:\n```git clone https://github.com/unclecode/groqcall.git```\n\n2. Navigate to the project directory:\n```cd groqcall```\n\n3. Create a virtual environment:\n```python -m venv venv```\n\n4. Activate virtual environment:\n```source venv/bin/activate```\n\n5. Install the required libraries:\n```pip install -r requirements.txt```\n\n6. Run the FastAPI server:\n```./venv/bin/uvicorn --app-dir app/ main:app --reload```\n\n\n## Using the Pre-built Server 🌐\nFor your convenience, I have already set up a server that you can use temporarily. This allows you to quickly start using the proxy without having to run it locally.\n\nTo use the pre-built server, simply make requests to the following base URL:\n```https://groqcall.ai/proxy/groq/v1```\n\n\n## Exploring GroqCall.ai 🚀\nThis README is organized into three main sections, each showcasing different aspects of GroqCall.ai:\n\n- **Sending POST Requests**: Here, I explore the functionality of sending direct POST requests to LLMs using GroqCall.ai. This section highlights the flexibility and control offered by the library when interacting with LLMs.\n- **FuncHub**: The second section introduces the concept of FuncHub, a useful feature that simplifies the process of executing functions. With FuncHub, there is no need to send the function JSON schema explicitly, as the functions are already hosted on the proxy server. This approach streamlines the workflow, allowing developers to obtain results with a single call without having to handle function call is production server.\n- **Using GroqCall with PhiData**: In this section, I demonstrate how GroqCall.ai can be seamlessly integrated with other libraries such as my favorite one, the PhiData library, leveraging its built-in tools to connect to LLMs and perform external tool requests.\n\n\n```python\n# The following libraries are optional if you're interested in using PhiData or managing your tools on the client side.\n!pip install phidata > /dev/null\n!pip install openai > /dev/null\n!pip install duckduckgo-search > /dev/null\n```\n\n## Sending POST request, with full functions implementation \n\n\n```python\nfrom duckduckgo_search import DDGS\nimport requests, os\nimport json\n\n# Here you pass your own GROQ API key\napi_key=userdata.get(\"GROQ_API_KEY\")\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"\n\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n\ndef duckduckgo_news(query, max_results=None):\n    \"\"\"\n    Use this function to get the latest news from DuckDuckGo.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n    \"duckduckgo_news\": duckduckgo_news,\n}\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_news\",\n                \"description\": \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\nif response.status_code == 200:\n    res = response.json()\n    message = res['choices'][0]['message']\n    tools_response_messages = []\n    if not message['content'] and 'tool_calls' in message:\n        for tool_call in message['tool_calls']:\n            tool_name = tool_call['function']['name']\n            tool_args = tool_call['function']['arguments']\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append({\n                \"role\": \"tool\", \"content\": json.dumps(tool_response)\n            })\n\n        if tools_response_messages:\n            request['messages'] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res['choices'][0]['message']['content'])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message['content'])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n\n```\n\n## Schema-less Function Call 🤩\nIn this method, we only need to provide the function's name, which consists of two parts, acting as a sort of namespace. The first part identifies the library or toolkit containing the functions, and the second part specifies the function's name, assuming it's already available on the proxy server. I aim to collaborate with the community to incorporate all typical functions, eliminating the need for passing a schema. Without having to handle function calls ourselves, a single request to the proxy enables it to identify and execute the functions, retrieve responses from large language models, and return the results to us. Thanks to Groq, all of this occurs in just seconds.\n\n\n```python\nfrom duckduckgo_search import DDGS\nimport requests, os\napi_key = userdata.get(\"GROQ_API_KEY\")\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"\n\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\",\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.\",\n        },\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.search\",\n            },\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\",\n            },\n        },\n    ],\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request,\n)\n\nif response.status_code == 200:\n    res = response.json()\n    print(res[\"choices\"][0][\"message\"][\"content\"])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n\n```\n\n## Using with PhiData\nFindData is a favorite of mine for creating AI assistants, thanks to its beautifully simplified interface, unlike the complexity seen in the LangChain library and LlamaIndex. I use it for many projects and want to give kudos to their team. It's open source, and I recommend everyone check it out. You can explore more from this link https://github.com/phidatahq/phidata.\n\n\n```python\nfrom google.README import userdata\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\nimport os, json\n\n\nmy_groq = OpenAILike(\n        model=\"mixtral-8x7b-32768\",\n        api_key=userdata.get(\"GROQ_API_KEY\"),\n        base_url=\"https://groqcall.ai/proxy/groq/v1\"\n    )\nassistant = Assistant(\n    llm=my_groq,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\nassistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n\n\n```\n\n## Contributions Welcome! 🙌\nI am excited to extend and grow this repository by adding more built-in functions and integrating additional services. If you are interested in contributing to this project and being a part of its development, I would love to collaborate with you! I plan to create a discord channel for this project, where we can discuss ideas, share knowledge, and work together to enhance the repository.\n\nHere's how you can get involved:\n\n1. Fork the repository and create your own branch.\n2. Implement new functions, integrate additional services, or make improvements to the existing codebase.\n3. Test your changes to ensure they work as expected.\n4. Submit a pull request describing the changes you have made and why they are valuable.\n\nIf you have any ideas, suggestions, or would like to discuss potential contributions, feel free to reach out to me. You can contact me through the following channels:\n\n- Twitter (X): @unclecode\n- Email: unclecode@kidocode.com\n\n### Copyright 2024 Unclecode (Hossein Tohidi)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\nI'm open to collaboration and excited to see how we can work together to enhance this project and provide value to the community. Let's connect and explore how we can help each other!\n\nTogether, let's make this repository even more awesome! 🚀\n"
  },
  {
    "path": "app/__init__.py",
    "content": ""
  },
  {
    "path": "app/config.py",
    "content": "# To be developed\nEVALUATION_CYCLES_COUNT=1\nPARSE_ERROR_TRIES = 5"
  },
  {
    "path": "app/functions/__init__.py",
    "content": ""
  },
  {
    "path": "app/functions/base.py",
    "content": "from pydantic import BaseModel\nfrom typing import Dict\n\nclass Function:\n    name: str\n    description: str\n\n    class Schema(BaseModel):\n        pass\n\n    @classmethod\n    def get_schema(cls) -> Dict:\n        schema_dict = {\n            \"name\": cls.name,\n            \"description\": cls.description,\n            \"parameters\": cls.Schema.schema(),\n        }\n        return schema_dict"
  },
  {
    "path": "app/functions/duckduck.py",
    "content": "from pydantic import BaseModel, Field\nfrom typing import Optional, Dict\nimport requests\nimport json\nfrom duckduckgo_search import DDGS\n\n# from .base import Function\nfrom pydantic import Field\nfrom typing import Optional\nimport requests\nimport json\n\nfrom .base import Function\n\nclass SearchFunction(Function):\n    name = \"duckduck.search\"\n    description = \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\"\n\n    class Schema(Function.Schema):\n        query: str = Field(..., description=\"The query to search for.\")\n        max_results: Optional[int] = Field(5, description=\"The maximum number of results to return.\")\n\n    @classmethod\n    def run(cls, **kwargs):\n        query = kwargs.get(\"query\")\n        max_results = kwargs.get(\"max_results\")\n        with DDGS() as ddgs:\n            return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n        \n\nclass NewsFunction(Function):\n    name = \"duckduck.news\"\n    description = \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\"\n\n    class Schema(Function.Schema):\n        query: str = Field(..., description=\"The query to search for.\")\n        max_results: Optional[int] = Field(5, description=\"The maximum number of results to return.\")\n\n    @classmethod\n    def run(cls, **kwargs):\n        query = kwargs.get(\"query\")\n        max_results = kwargs.get(\"max_results\")\n\n        with DDGS() as ddgs:\n            results = [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n        return results"
  },
  {
    "path": "app/libs/__init__.py",
    "content": "from .context import Context\nfrom .base_handler import Handler, DefaultCompletionHandler, ExceptionHandler, FallbackHandler\nfrom .provider_handler import ProviderSelectionHandler\nfrom .vision_handler import ImageMessageHandler\nfrom .tools_handler import ToolExtractionHandler, ToolResponseHandler\n\n__all__ = [\n    \"Context\",\n    \"Handler\",\n    \"DefaultCompletionHandler\",\n    \"ExceptionHandler\",\n    \"ProviderSelectionHandler\",\n    \"ImageMessageHandler\",\n    \"ToolExtractionHandler\",\n    \"ToolResponseHandler\",\n    \"FallbackHandler\",\n]\n"
  },
  {
    "path": "app/libs/base_handler.py",
    "content": "from abc import ABC, abstractmethod\nfrom .context import Context\nfrom fastapi.responses import JSONResponse\nimport traceback\n\nclass Handler(ABC):\n    \"\"\"Abstract Handler class for building the chain of handlers.\"\"\"\n\n    _next_handler: \"Handler\" = None\n\n    def set_next(self, handler: \"Handler\") -> \"Handler\":\n        self._next_handler = handler\n        return handler\n\n    @abstractmethod\n    async def handle(self, context: Context):\n        if self._next_handler:\n            try:\n                return await self._next_handler.handle(context)\n            except Exception as e:\n                _exception_handler: \"Handler\" = ExceptionHandler()\n                # Extract the stack trace and log the exception\n                return await _exception_handler.handle(self._next_handler, context, e)\n\n\nclass DefaultCompletionHandler(Handler):\n    async def handle(self, context: Context):\n        if context.is_normal_chat:\n            # Assuming context.client is set and has a method for creating chat completions\n            completion = context.client.route(\n                messages=context.messages,\n                **context.client.clean_params(context.params),\n            )\n            context.response = completion.model_dump()\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n\nclass FallbackHandler(Handler):\n    async def handle(self, context: Context):\n        # This handler does not pass the request further down the chain.\n        # It acts as a fallback when no other handler has processed the request.\n        if not context.response:\n            # The default action when no other handlers have processed the request\n            context.response = {\"message\": \"No suitable action found for the request.\"}\n            return JSONResponse(content=context.response, status_code=400)\n\n        # If there's already a response set in the context, it means one of the handlers has processed the request.\n        return JSONResponse(content=context.response, status_code=200)\n\n\nclass ExceptionHandler(Handler):\n    async def handle(self, handler: Handler, context: Context, exception: Exception):\n        print(f\"Error processing the request: {str(handler.__class__) } - {exception}\")\n        # print(traceback.format_exc())\n        return JSONResponse(\n            content={\"error\": \"An unexpected error occurred, within handler \" + str(handler.__class__) + \" : \" + str(exception)},\n            status_code=500,\n        )\n\n\n"
  },
  {
    "path": "app/libs/chains copy.py",
    "content": "from abc import ABC, abstractmethod\nfrom typing import Any, Dict\nfrom importlib import import_module\nimport json\nimport uuid\nimport traceback\nfrom fastapi import Request\nfrom fastapi.responses import JSONResponse\nfrom providers import BaseProvider\nfrom prompts import *\nfrom providers import GroqProvider\nimport importlib\nfrom utils import get_tool_call_response, create_logger, describe\n\nmissed_tool_logger = create_logger(\n    \"chain.missed_tools\", \".logs/empty_tool_tool_response.log\"\n)\n\n\nclass Context:\n    def __init__(self, request: Request, provider: str, body: Dict[str, Any]):\n        self.request = request\n        self.provider = provider\n        self.body = body\n        self.response = None\n\n        # extract all keys from body except messages and tools and set in params\n        self.params = {k: v for k, v in body.items() if k not in [\"messages\", \"tools\"]}\n\n        # self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"return\")\n        self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"forward\")\n        self.params.pop(\"no_tool_behaviour\", None)\n\n        # Todo: For now, no stream, sorry ;)\n        self.params[\"stream\"] = False\n\n        self.messages = body.get(\"messages\", [])\n        self.tools = body.get(\"tools\", [])\n\n        self.builtin_tools = [\n            t for t in self.tools if \"parameters\" not in t[\"function\"]\n        ]\n        self.builtin_tool_names = [t[\"function\"][\"name\"] for t in self.builtin_tools]\n        self.custom_tools = [t for t in self.tools if \"parameters\" in t[\"function\"]]\n\n        for bt in self.builtin_tools:\n            func_namespace = bt[\"function\"][\"name\"]\n            if len(func_namespace.split(\".\")) == 2:\n                module_name, func_class_name = func_namespace.split(\".\")\n                func_class_name = f\"{func_class_name.capitalize()}Function\"\n                # raise ValueError(\"Only one builtin function can be called at a time.\")\n                module = importlib.import_module(f\"app.functions.{module_name}\")\n                func_class = getattr(module, func_class_name, None)\n                schema_dict = func_class.get_schema()\n                if schema_dict:\n                    bt[\"function\"] = schema_dict\n                    bt[\"run\"] = func_class.run\n                    bt[\"extra\"] = self.params.get(\"extra\", {})\n                    self.params.pop(\"extra\", None)\n\n        self.client: BaseProvider = None\n\n    @property\n    def last_message(self):\n        return self.messages[-1] if self.messages else {}\n\n    @property\n    def is_tool_call(self):\n        return bool(\n            self.last_message[\"role\"] == \"user\"\n            and self.tools\n            and self.params.get(\"tool_choice\", \"none\") != \"none\"\n        )\n\n    @property\n    def is_tool_response(self):\n        return bool(self.last_message[\"role\"] == \"tool\" and self.tools)\n\n    @property\n    def is_normal_chat(self):\n        return bool(not self.is_tool_call and not self.is_tool_response)\n\n\nclass Handler(ABC):\n    \"\"\"Abstract Handler class for building the chain of handlers.\"\"\"\n\n    _next_handler: \"Handler\" = None\n\n    def set_next(self, handler: \"Handler\") -> \"Handler\":\n        self._next_handler = handler\n        return handler\n\n    @abstractmethod\n    async def handle(self, context: Context):\n        if self._next_handler:\n            try:\n                return await self._next_handler.handle(context)\n            except Exception as e:\n                _exception_handler: \"Handler\" = ExceptionHandler()\n                # Extract the stack trace and log the exception\n                return await _exception_handler.handle(context, e)\n\n\nclass ProviderSelectionHandler(Handler):\n    @staticmethod\n    def provider_exists(provider: str) -> bool:\n        module_name = f\"app.providers\"\n        class_name = f\"{provider.capitalize()}Provider\"\n        try:\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n            return bool(provider_class)\n        except ImportError:\n            return False\n\n    async def handle(self, context: Context):\n        # Construct the module path and class name based on the provider\n        module_name = f\"app.providers\"\n        class_name = f\"{context.provider.capitalize()}Provider\"\n\n        try:\n            # Dynamically import the module and class\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n\n            if provider_class:\n                context.client = provider_class(\n                    api_key=context.api_token\n                )  # Assuming an api_key parameter\n                return await super().handle(context)\n            else:\n                raise ValueError(\n                    f\"Provider class {class_name} could not be found in {module_name}.\"\n                )\n        except ImportError as e:\n            # Handle import error (e.g., module or class not found)\n            print(f\"Error importing {class_name} from {module_name}: {e}\")\n            context.response = {\n                \"error\": f\"An error occurred while trying to load the provider: {e}\"\n            }\n            return JSONResponse(content=context.response, status_code=500)\n\n\nclass ImageMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    prompt = None\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            # new_messages.append({\"role\": message[\"role\"], \"content\": content[\"text\"]})\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                prompt = prompt or IMAGE_DESCRIPTO_PROMPT\n                                description = describe(prompt, image_url)\n                                if description:\n                                    description = get_image_desc_guide(image_ref, description)\n                                    new_messages.append(\n                                        {\"role\": message[\"role\"], \"content\": description}\n                                    )\n                                    image_ref += 1\n                                else:\n                                    pass\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n                else:\n                    new_messages.append(message)\n            else:\n                new_messages.append(message)\n\n        context.messages = new_messages\n        return await super().handle(context)\n    \n\nclass ImageLLavaMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            new_messages.append(message)\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                description = describe(prompt, image_url)\n                                new_messages.append(\n                                    {\"role\": \"assistant\", \"content\": description}\n                                )\n                                image_ref += 1\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n        context.messages = new_messages\n        return await super().handle(context)\n\n\nclass ToolExtractionHandler(Handler):\n    async def handle(self, context: Context):\n        body = context.body\n        if context.is_tool_call:\n\n            # Prepare the messages and tools for the tool extraction\n            messages = [\n                f\"{m['role'].title()}: {m['content']}\"\n                for m in context.messages\n                if m[\"role\"] != \"system\"\n            ]\n            tools_json = json.dumps([t[\"function\"] for t in context.tools], indent=4)\n\n            # Process the tool_choice\n            tool_choice = context.params.get(\"tool_choice\", \"auto\")\n            forced_mode = False\n            if (\n                type(tool_choice) == dict\n                and tool_choice.get(\"type\", None) == \"function\"\n            ):\n                tool_choice = tool_choice[\"function\"].get(\"name\", None)\n                if not tool_choice:\n                    raise ValueError(\n                        \"Invalid tool choice. 'tool_choice' is set to a dictionary with 'type' as 'function', but 'function' does not have a 'name' key.\"\n                    )\n                forced_mode = True\n\n                # Regenerate the string tool_json and keep only the forced tool\n                tools_json = json.dumps(\n                    [\n                        t[\"function\"]\n                        for t in context.tools\n                        if t[\"function\"][\"name\"] == tool_choice\n                    ],\n                    indent=4,\n                )\n\n            system_message = (\n                SYSTEM_MESSAGE if not forced_mode else ENFORCED_SYSTAME_MESSAE\n            )\n            suffix = SUFFIX if not forced_mode else get_forced_tool_suffix(tool_choice)\n\n            new_messages = [\n                {\"role\": \"system\", \"content\": system_message},\n                {\n                    \"role\": \"system\",\n                    \"content\": f\"Conversation History:\\n{''.join(messages)}\\n\\nTools: \\n{tools_json}\\n\\n{suffix}\",\n                },\n            ]\n\n            completion, tool_calls = await self.process_tool_calls(\n                context, new_messages\n            )\n\n            if not tool_calls:\n                if context.no_tool_behaviour == \"forward\":\n                    context.tools = None\n                    return await super().handle(context)\n                else:\n                    context.response = {\"tool_calls\": []}\n                    tool_response = get_tool_call_response(completion, [], [])\n                    missed_tool_logger.debug(\n                        f\"Last message content: {context.last_message['content']}\"\n                    )\n                    return JSONResponse(content=tool_response, status_code=200)\n\n            unresolved_tol_calls = [\n                t\n                for t in tool_calls\n                if t[\"function\"][\"name\"] not in context.builtin_tool_names\n            ]\n            resolved_responses = []\n            for tool in tool_calls:\n                for bt in context.builtin_tools:\n                    if tool[\"function\"][\"name\"] == bt[\"function\"][\"name\"]:\n                        res = bt[\"run\"](\n                            **{\n                                **json.loads(tool[\"function\"][\"arguments\"]),\n                                **bt[\"extra\"],\n                            }\n                        )\n                        resolved_responses.append(\n                            {\n                                \"name\": tool[\"function\"][\"name\"],\n                                \"role\": \"tool\",\n                                \"content\": json.dumps(res),\n                                \"tool_call_id\": \"chatcmpl-\" + completion.id,\n                            }\n                        )\n\n                if not unresolved_tol_calls:\n                    context.messages.extend(resolved_responses)\n                    return await super().handle(context)\n\n            tool_response = get_tool_call_response(\n                completion, unresolved_tol_calls, resolved_responses\n            )\n\n            context.response = tool_response\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n    async def process_tool_calls(self, context, new_messages):\n        try:\n            tries = 5\n            tool_calls = []\n            while tries > 0:\n                try:\n                    # Assuming the context has an instantiated client according to the selected provider\n                    completion = context.client.route(\n                        model=context.client.parser_model,\n                        messages=new_messages,\n                        temperature=0,\n                        max_tokens=1024,\n                        top_p=1,\n                        stream=False,\n                    )\n\n                    response = completion.choices[0].message.content\n                    if \"```json\" in response:\n                        response = response.split(\"```json\")[1].split(\"```\")[0]\n\n                    try:\n                        tool_response = json.loads(response)\n                        if isinstance(tool_response, list):\n                            tool_response = {\"tool_calls\": tool_response}\n                    except json.JSONDecodeError as e:\n                        print(\n                            f\"Error parsing the tool response: {e}, tries left: {tries}\"\n                        )\n                        new_messages.append(\n                            {\n                                \"role\": \"user\",\n                                \"content\": f\"Error: {e}.\\n\\n{CLEAN_UP_MESSAGE}\",\n                            }\n                        )\n                        tries -= 1\n                        continue\n\n                    for func in tool_response.get(\"tool_calls\", []):\n                        tool_calls.append(\n                            {\n                                \"id\": f\"call_{func['name']}_{str(uuid.uuid4())}\",\n                                \"type\": \"function\",\n                                \"function\": {\n                                    \"name\": func[\"name\"],\n                                    \"arguments\": json.dumps(func[\"arguments\"]),\n                                },\n                            }\n                        )\n\n                    break\n                except Exception as e:\n                    raise e\n\n            if tries == 0:\n                tool_calls = []\n\n            return completion, tool_calls\n        except Exception as e:\n            print(f\"Error processing the tool calls: {e}\")\n            raise e\n\n\nclass ToolResponseHandler(Handler):\n    async def handle(self, context: Context):\n        body = context.body\n        if context.is_tool_response:\n            messages = context.messages\n\n            for message in messages:\n                if message[\"role\"] == \"tool\":\n                    message[\"role\"] = \"user\"\n                    message[\"content\"] = get_func_result_guide(message[\"content\"])\n\n            messages[-1][\"role\"] = \"user\"\n            # Assuming get_func_result_guide is a function that formats the tool response\n            messages[-1][\"content\"] = get_func_result_guide(messages[-1][\"content\"])\n\n            try:\n                completion = context.client.route(\n                    messages=messages,\n                    **context.client.clean_params(context.params),\n                )\n                context.response = completion.model_dump()\n                return JSONResponse(content=context.response, status_code=200)\n            except Exception as e:\n                # Log the exception or handle it as needed\n                print(e)\n                context.response = {\n                    \"error\": \"An error occurred processing the tool response\"\n                }\n                return JSONResponse(content=context.response, status_code=500)\n\n        return await super().handle(context)\n\n\nclass DefaultCompletionHandler(Handler):\n    async def handle(self, context: Context):\n        if context.is_normal_chat:\n            # Assuming context.client is set and has a method for creating chat completions\n            completion = context.client.route(\n                messages=context.messages,\n                **context.client.clean_params(context.params),\n            )\n            context.response = completion.model_dump()\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n\nclass FallbackHandler(Handler):\n    async def handle(self, context: Context):\n        # This handler does not pass the request further down the chain.\n        # It acts as a fallback when no other handler has processed the request.\n        if not context.response:\n            # The default action when no other handlers have processed the request\n            context.response = {\"message\": \"No suitable action found for the request.\"}\n            return JSONResponse(content=context.response, status_code=400)\n\n        # If there's already a response set in the context, it means one of the handlers has processed the request.\n        return JSONResponse(content=context.response, status_code=200)\n\n\nclass ExceptionHandler(Handler):\n    async def handle(self, context: Context, exception: Exception):\n        print(f\"Error processing the request: {exception}\")\n        print(traceback.format_exc())\n        return JSONResponse(\n            content={\"error\": \"An unexpected error occurred. \" + str(exception)},\n            status_code=500,\n        )\n"
  },
  {
    "path": "app/libs/chains.py",
    "content": "from abc import ABC, abstractmethod\nfrom typing import Any, Dict\nfrom importlib import import_module\nimport json\nimport uuid\nimport traceback\nfrom fastapi import Request\nfrom fastapi.responses import JSONResponse\nfrom providers import BaseProvider\nfrom prompts import *\nfrom providers import GroqProvider\nimport importlib\nfrom utils import get_tool_call_response, create_logger, describe\n\nmissed_tool_logger = create_logger(\n    \"chain.missed_tools\", \".logs/empty_tool_tool_response.log\"\n)\n\n\nclass Context:\n    def __init__(self, request: Request, provider: str, body: Dict[str, Any]):\n        self.request = request\n        self.provider = provider\n        self.body = body\n        self.response = None\n\n        # extract all keys from body except messages and tools and set in params\n        self.params = {k: v for k, v in body.items() if k not in [\"messages\", \"tools\"]}\n\n        # self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"return\")\n        self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"forward\")\n        self.params.pop(\"no_tool_behaviour\", None)\n\n        # Todo: For now, no stream, sorry ;)\n        self.params[\"stream\"] = False\n\n        self.messages = body.get(\"messages\", [])\n        self.tools = body.get(\"tools\", [])\n\n        self.builtin_tools = [\n            t for t in self.tools if \"parameters\" not in t[\"function\"]\n        ]\n        self.builtin_tool_names = [t[\"function\"][\"name\"] for t in self.builtin_tools]\n        self.custom_tools = [t for t in self.tools if \"parameters\" in t[\"function\"]]\n\n        for bt in self.builtin_tools:\n            func_namespace = bt[\"function\"][\"name\"]\n            if len(func_namespace.split(\".\")) == 2:\n                module_name, func_class_name = func_namespace.split(\".\")\n                func_class_name = f\"{func_class_name.capitalize()}Function\"\n                # raise ValueError(\"Only one builtin function can be called at a time.\")\n                module = importlib.import_module(f\"app.functions.{module_name}\")\n                func_class = getattr(module, func_class_name, None)\n                schema_dict = func_class.get_schema()\n                if schema_dict:\n                    bt[\"function\"] = schema_dict\n                    bt[\"run\"] = func_class.run\n                    bt[\"extra\"] = self.params.get(\"extra\", {})\n                    self.params.pop(\"extra\", None)\n\n        self.client: BaseProvider = None\n\n    @property\n    def last_message(self):\n        return self.messages[-1] if self.messages else {}\n\n    @property\n    def is_tool_call(self):\n        return bool(\n            self.last_message[\"role\"] == \"user\"\n            and self.tools\n            and self.params.get(\"tool_choice\", \"none\") != \"none\"\n        )\n\n    @property\n    def is_tool_response(self):\n        return bool(self.last_message[\"role\"] == \"tool\" and self.tools)\n\n    @property\n    def is_normal_chat(self):\n        return bool(not self.is_tool_call and not self.is_tool_response)\n\n\nclass Handler(ABC):\n    \"\"\"Abstract Handler class for building the chain of handlers.\"\"\"\n\n    _next_handler: \"Handler\" = None\n\n    def set_next(self, handler: \"Handler\") -> \"Handler\":\n        self._next_handler = handler\n        return handler\n\n    @abstractmethod\n    async def handle(self, context: Context):\n        if self._next_handler:\n            try:\n                return await self._next_handler.handle(context)\n            except Exception as e:\n                _exception_handler: \"Handler\" = ExceptionHandler()\n                # Extract the stack trace and log the exception\n                return await _exception_handler.handle(context, e)\n\n\nclass ProviderSelectionHandler(Handler):\n    @staticmethod\n    def provider_exists(provider: str) -> bool:\n        module_name = f\"app.providers\"\n        class_name = f\"{provider.capitalize()}Provider\"\n        try:\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n            return bool(provider_class)\n        except ImportError:\n            return False\n\n    async def handle(self, context: Context):\n        # Construct the module path and class name based on the provider\n        module_name = f\"app.providers\"\n        class_name = f\"{context.provider.capitalize()}Provider\"\n\n        try:\n            # Dynamically import the module and class\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n\n            if provider_class:\n                context.client = provider_class(\n                    api_key=context.api_token\n                )  # Assuming an api_key parameter\n                return await super().handle(context)\n            else:\n                raise ValueError(\n                    f\"Provider class {class_name} could not be found in {module_name}.\"\n                )\n        except ImportError as e:\n            # Handle import error (e.g., module or class not found)\n            print(f\"Error importing {class_name} from {module_name}: {e}\")\n            context.response = {\n                \"error\": f\"An error occurred while trying to load the provider: {e}\"\n            }\n            return JSONResponse(content=context.response, status_code=500)\n\n\nclass ImageMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    prompt = None\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            # new_messages.append({\"role\": message[\"role\"], \"content\": content[\"text\"]})\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                prompt = prompt or IMAGE_DESCRIPTO_PROMPT\n                                description = describe(prompt, image_url)\n                                if description:\n                                    description = get_image_desc_guide(image_ref, description)\n                                    new_messages.append(\n                                        {\"role\": message[\"role\"], \"content\": description}\n                                    )\n                                    image_ref += 1\n                                else:\n                                    pass\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n                else:\n                    new_messages.append(message)\n            else:\n                new_messages.append(message)\n\n        context.messages = new_messages\n        return await super().handle(context)\n    \n\nclass ImageLLavaMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            new_messages.append(message)\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                description = describe(prompt, image_url)\n                                new_messages.append(\n                                    {\"role\": \"assistant\", \"content\": description}\n                                )\n                                image_ref += 1\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n        context.messages = new_messages\n        return await super().handle(context)\n\n\nclass ToolExtractionHandler(Handler):\n    async def handle(self, context: Context):\n        body = context.body\n        if context.is_tool_call:\n\n            # Prepare the messages and tools for the tool extraction\n            messages = [\n                f\"{m['role'].title()}: {m['content']}\"\n                for m in context.messages\n                if m[\"role\"] != \"system\"\n            ]\n            tools_json = json.dumps([t[\"function\"] for t in context.tools], indent=4)\n\n            # Process the tool_choice\n            tool_choice = context.params.get(\"tool_choice\", \"auto\")\n            forced_mode = False\n            if (\n                type(tool_choice) == dict\n                and tool_choice.get(\"type\", None) == \"function\"\n            ):\n                tool_choice = tool_choice[\"function\"].get(\"name\", None)\n                if not tool_choice:\n                    raise ValueError(\n                        \"Invalid tool choice. 'tool_choice' is set to a dictionary with 'type' as 'function', but 'function' does not have a 'name' key.\"\n                    )\n                forced_mode = True\n\n                # Regenerate the string tool_json and keep only the forced tool\n                tools_json = json.dumps(\n                    [\n                        t[\"function\"]\n                        for t in context.tools\n                        if t[\"function\"][\"name\"] == tool_choice\n                    ],\n                    indent=4,\n                )\n\n            system_message = (\n                SYSTEM_MESSAGE if not forced_mode else ENFORCED_SYSTAME_MESSAE\n            )\n            suffix = SUFFIX if not forced_mode else get_forced_tool_suffix(tool_choice)\n\n            new_messages = [\n                {\"role\": \"system\", \"content\": system_message},\n                {\n                    \"role\": \"system\",\n                    \"content\": f\"Conversation History:\\n{''.join(messages)}\\n\\nTools: \\n{tools_json}\\n\\n{suffix}\",\n                },\n            ]\n\n            completion, tool_calls = await self.process_tool_calls(\n                context, new_messages\n            )\n\n            if not tool_calls:\n                if context.no_tool_behaviour == \"forward\":\n                    context.tools = None\n                    return await super().handle(context)\n                else:\n                    context.response = {\"tool_calls\": []}\n                    tool_response = get_tool_call_response(completion, [], [])\n                    missed_tool_logger.debug(\n                        f\"Last message content: {context.last_message['content']}\"\n                    )\n                    return JSONResponse(content=tool_response, status_code=200)\n\n            unresolved_tol_calls = [\n                t\n                for t in tool_calls\n                if t[\"function\"][\"name\"] not in context.builtin_tool_names\n            ]\n            resolved_responses = []\n            for tool in tool_calls:\n                for bt in context.builtin_tools:\n                    if tool[\"function\"][\"name\"] == bt[\"function\"][\"name\"]:\n                        res = bt[\"run\"](\n                            **{\n                                **json.loads(tool[\"function\"][\"arguments\"]),\n                                **bt[\"extra\"],\n                            }\n                        )\n                        resolved_responses.append(\n                            {\n                                \"name\": tool[\"function\"][\"name\"],\n                                \"role\": \"tool\",\n                                \"content\": json.dumps(res),\n                                \"tool_call_id\": \"chatcmpl-\" + completion.id,\n                            }\n                        )\n\n                if not unresolved_tol_calls:\n                    context.messages.extend(resolved_responses)\n                    return await super().handle(context)\n\n            tool_response = get_tool_call_response(\n                completion, unresolved_tol_calls, resolved_responses\n            )\n\n            context.response = tool_response\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n    async def process_tool_calls(self, context, new_messages):\n        try:\n            tries = 5\n            tool_calls = []\n            while tries > 0:\n                try:\n                    # Assuming the context has an instantiated client according to the selected provider\n                    completion = context.client.route(\n                        model=context.client.parser_model,\n                        messages=new_messages,\n                        temperature=0,\n                        max_tokens=1024,\n                        top_p=1,\n                        stream=False,\n                    )\n\n                    response = completion.choices[0].message.content\n                    if \"```json\" in response:\n                        response = response.split(\"```json\")[1].split(\"```\")[0]\n\n                    try:\n                        tool_response = json.loads(response)\n                        if isinstance(tool_response, list):\n                            tool_response = {\"tool_calls\": tool_response}\n                    except json.JSONDecodeError as e:\n                        print(\n                            f\"Error parsing the tool response: {e}, tries left: {tries}\"\n                        )\n                        new_messages.append(\n                            {\n                                \"role\": \"user\",\n                                \"content\": f\"Error: {e}.\\n\\n{CLEAN_UP_MESSAGE}\",\n                            }\n                        )\n                        tries -= 1\n                        continue\n\n                    for func in tool_response.get(\"tool_calls\", []):\n                        tool_calls.append(\n                            {\n                                \"id\": f\"call_{func['name']}_{str(uuid.uuid4())}\",\n                                \"type\": \"function\",\n                                \"function\": {\n                                    \"name\": func[\"name\"],\n                                    \"arguments\": json.dumps(func[\"arguments\"]),\n                                },\n                            }\n                        )\n\n                    break\n                except Exception as e:\n                    raise e\n\n            if tries == 0:\n                tool_calls = []\n\n            return completion, tool_calls\n        except Exception as e:\n            print(f\"Error processing the tool calls: {e}\")\n            raise e\n\n\nclass ToolResponseHandler(Handler):\n    async def handle(self, context: Context):\n        body = context.body\n        if context.is_tool_response:\n            messages = context.messages\n\n            for message in messages:\n                if message[\"role\"] == \"tool\":\n                    message[\"role\"] = \"user\"\n                    message[\"content\"] = get_func_result_guide(message[\"content\"])\n\n            messages[-1][\"role\"] = \"user\"\n            # Assuming get_func_result_guide is a function that formats the tool response\n            messages[-1][\"content\"] = get_func_result_guide(messages[-1][\"content\"])\n\n            try:\n                completion = context.client.route(\n                    messages=messages,\n                    **context.client.clean_params(context.params),\n                )\n                context.response = completion.model_dump()\n                return JSONResponse(content=context.response, status_code=200)\n            except Exception as e:\n                # Log the exception or handle it as needed\n                print(e)\n                context.response = {\n                    \"error\": \"An error occurred processing the tool response\"\n                }\n                return JSONResponse(content=context.response, status_code=500)\n\n        return await super().handle(context)\n\n\nclass DefaultCompletionHandler(Handler):\n    async def handle(self, context: Context):\n        if context.is_normal_chat:\n            # Assuming context.client is set and has a method for creating chat completions\n            completion = context.client.route(\n                messages=context.messages,\n                **context.client.clean_params(context.params),\n            )\n            context.response = completion.model_dump()\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n\nclass FallbackHandler(Handler):\n    async def handle(self, context: Context):\n        # This handler does not pass the request further down the chain.\n        # It acts as a fallback when no other handler has processed the request.\n        if not context.response:\n            # The default action when no other handlers have processed the request\n            context.response = {\"message\": \"No suitable action found for the request.\"}\n            return JSONResponse(content=context.response, status_code=400)\n\n        # If there's already a response set in the context, it means one of the handlers has processed the request.\n        return JSONResponse(content=context.response, status_code=200)\n\n\nclass ExceptionHandler(Handler):\n    async def handle(self, context: Context, exception: Exception):\n        print(f\"Error processing the request: {exception}\")\n        print(traceback.format_exc())\n        return JSONResponse(\n            content={\"error\": \"An unexpected error occurred. \" + str(exception)},\n            status_code=500,\n        )\n"
  },
  {
    "path": "app/libs/context.py",
    "content": "from typing import Any, Dict\nfrom fastapi import Request\nfrom providers import BaseProvider\nfrom prompts import *\nimport importlib\nfrom utils import create_logger\n\n\nclass Context:\n    def __init__(self, request: Request, provider: str, body: Dict[str, Any]):\n        self.request = request\n        self.provider = provider\n        self.body = body\n        self.response = None\n\n        # extract all keys from body except messages and tools and set in params\n        self.params = {k: v for k, v in body.items() if k not in [\"messages\", \"tools\"]}\n\n        # self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"return\")\n        self.no_tool_behaviour = self.params.get(\"no_tool_behaviour\", \"forward\")\n        self.params.pop(\"no_tool_behaviour\", None)\n\n        # Todo: For now, no stream, sorry ;)\n        self.params[\"stream\"] = False\n\n        self.messages = body.get(\"messages\", [])\n        self.tools = body.get(\"tools\", [])\n\n        self.builtin_tools = [\n            t for t in self.tools if \"parameters\" not in t[\"function\"]\n        ]\n        self.builtin_tool_names = [t[\"function\"][\"name\"] for t in self.builtin_tools]\n        self.custom_tools = [t for t in self.tools if \"parameters\" in t[\"function\"]]\n\n        for bt in self.builtin_tools:\n            func_namespace = bt[\"function\"][\"name\"]\n            if len(func_namespace.split(\".\")) == 2:\n                module_name, func_class_name = func_namespace.split(\".\")\n                func_class_name = f\"{func_class_name.capitalize()}Function\"\n                # raise ValueError(\"Only one builtin function can be called at a time.\")\n                module = importlib.import_module(f\"app.functions.{module_name}\")\n                func_class = getattr(module, func_class_name, None)\n                schema_dict = func_class.get_schema()\n                if schema_dict:\n                    bt[\"function\"] = schema_dict\n                    bt[\"run\"] = func_class.run\n                    bt[\"extra\"] = self.params.get(\"extra\", {})\n                    self.params.pop(\"extra\", None)\n\n        self.client: BaseProvider = None\n\n    @property\n    def last_message(self):\n        return self.messages[-1] if self.messages else {}\n\n    @property\n    def is_tool_call(self):\n        return bool(\n            self.last_message[\"role\"] == \"user\"\n            and self.tools\n            and self.params.get(\"tool_choice\", None) != \"none\"\n        )\n\n    @property\n    def is_tool_response(self):\n        return bool(self.last_message[\"role\"] == \"tool\" and self.tools)\n\n    @property\n    def is_normal_chat(self):\n        return bool(not self.is_tool_call and not self.is_tool_response)\n"
  },
  {
    "path": "app/libs/provider_handler.py",
    "content": "from importlib import import_module\nfrom fastapi.responses import JSONResponse\nfrom prompts import *\nfrom .base_handler import Handler\nfrom .context import Context\n\nclass ProviderSelectionHandler(Handler):\n    @staticmethod\n    def provider_exists(provider: str) -> bool:\n        module_name = f\"app.providers\"\n        class_name = f\"{provider.capitalize()}Provider\"\n        try:\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n            return bool(provider_class)\n        except ImportError:\n            return False\n\n    async def handle(self, context: Context):\n        # Construct the module path and class name based on the provider\n        module_name = f\"app.providers\"\n        class_name = f\"{context.provider.capitalize()}Provider\"\n\n        try:\n            # Dynamically import the module and class\n            provider_module = import_module(module_name)\n            provider_class = getattr(provider_module, class_name)\n\n            if provider_class:\n                context.client = provider_class(\n                    api_key=context.api_token\n                )  # Assuming an api_key parameter\n                return await super().handle(context)\n            else:\n                raise ValueError(\n                    f\"Provider class {class_name} could not be found in {module_name}.\"\n                )\n        except ImportError as e:\n            # Handle import error (e.g., module or class not found)\n            print(f\"Error importing {class_name} from {module_name}: {e}\")\n            context.response = {\n                \"error\": f\"An error occurred while trying to load the provider: {e}\"\n            }\n            return JSONResponse(content=context.response, status_code=500)\n\n"
  },
  {
    "path": "app/libs/tools_handler.py",
    "content": "import concurrent.futures\nimport uuid\nimport json\nimport math\nfrom fastapi.responses import JSONResponse\nfrom prompts import *\nfrom .base_handler import Handler, Context\nfrom .context import Context\nfrom utils import get_tool_call_response, create_logger, describe\nfrom config import PARSE_ERROR_TRIES, EVALUATION_CYCLES_COUNT\nfrom collections import defaultdict\n\nmissed_tool_logger = create_logger(\n    \"chain.missed_tools\", \".logs/empty_tool_tool_response.log\"\n)\n\n\nclass ImageLLavaMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            new_messages.append(message)\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                description = describe(prompt, image_url)\n                                new_messages.append(\n                                    {\"role\": \"assistant\", \"content\": description}\n                                )\n                                image_ref += 1\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n        context.messages = new_messages\n        return await super().handle(context)\n\n\nclass ToolExtractionHandler(Handler):\n    async def handle(self, context: Context):\n            if not context.is_tool_call:\n                return await super().handle(context)\n\n            # Step 1: Prepare the conversation history\n            messages = self._prepare_conversation_history(context.messages)\n\n            # Step 2: Prepare tool details and detect the mode of operation\n            available_tools, system_message, suffix = self._prepare_tool_details(context)\n\n            # Step 3: Prepare the messages for the model\n            new_messages = self._prepare_model_messages(messages, available_tools, suffix, context.messages[-1]['content'], system_message)\n\n            # Step 4: Detect the tool calls\n            tool_calls_result = await self.process_tool_calls(context, new_messages)\n            tool_calls = tool_calls_result[\"tool_calls\"]\n\n            # Step 5: Handle the situation where no tool calls are detected\n            if not tool_calls:\n                return await self._handle_no_tool_calls(context, tool_calls_result)\n\n            # Step 6: Process built-in tools and resolve the tool calls\n            unresolved_tool_calls, resolved_responses = self._process_builtin_tools(context, tool_calls, tool_calls_result[\"last_completion\"].id)\n\n            if not unresolved_tool_calls:\n                context.messages.extend(resolved_responses)\n                return await super().handle(context)\n\n            # Step 7: Return the unresolved tool calls to the client\n            tool_response = get_tool_call_response(tool_calls_result, unresolved_tool_calls, resolved_responses)\n            context.response = tool_response\n            return JSONResponse(content=context.response, status_code=200)\n\n    def _prepare_conversation_history(self, messages):\n        return [\n            f\"<{m['role'].lower()}>\\n{m['content']}\\n</{m['role'].lower()}>\"\n            for m in messages\n            if m[\"role\"] != \"system\"\n        ]\n\n    def _prepare_tool_details(self, context):\n        tool_choice = context.params.get(\"tool_choice\", \"auto\")\n        forced_mode = type(tool_choice) == dict and tool_choice.get(\"type\", None) == \"function\"\n        available_tools = []\n\n        if forced_mode:\n            tool_choice = tool_choice[\"function\"][\"name\"]\n            available_tools = [t[\"function\"] for t in context.tools if t[\"function\"][\"name\"] == tool_choice]\n            system_message = ENFORCED_SYSTAME_MESSAE\n            suffix = get_forced_tool_suffix(tool_choice)\n        else:\n            tool_choice = \"auto\"\n            available_tools = [t[\"function\"] for t in context.tools]\n            system_message = SYSTEM_MESSAGE\n            suffix = get_suffix()\n\n        # Add one special tool called \"fallback\", which is always available, its job is to be used when non of other tools are useful for the user input.\n        # available_tools.append({\n        #     \"name\": \"fallback\", \n        #     \"description\": \"Use this tool when none of the other tools are useful for the user input.\",\n        #     \"arguments\": {}}\n        # )\n\n        return available_tools, system_message, suffix\n\n    def _prepare_model_messages(self, messages, available_tools, suffix, last_message_content, system_message):\n        messages_flatten = \"\\n\".join(messages)\n        tools_json = json.dumps(available_tools, indent=4)\n\n        return [\n            {\"role\": \"system\", \"content\": system_message},\n            {\n                \"role\": \"user\",\n                \"content\": f\"# Conversation History:\\n{messages_flatten}\\n\\n# Available Tools: \\n{tools_json}\\n\\n{suffix}\\n{last_message_content}\",\n            },\n        ]\n\n    async def _handle_no_tool_calls(self, context, tool_calls_result):\n        if context.no_tool_behaviour == \"forward\":\n            context.tools = None\n            return await super().handle(context)\n        else:\n            context.response = {\"tool_calls\": []}\n            tool_response = get_tool_call_response(tool_calls_result, [], [])\n            missed_tool_logger.debug(f\"Last message content: {context.last_message['content']}\")\n            return JSONResponse(content=tool_response, status_code=200)\n\n    def _process_builtin_tools(self, context, tool_calls, tool_calls_result_id):\n        unresolved_tool_calls = [\n            t\n            for t in tool_calls\n            if t[\"function\"][\"name\"] not in context.builtin_tool_names\n        ]\n        resolved_responses = []\n\n        for tool in tool_calls:\n            for bt in context.builtin_tools:\n                if tool[\"function\"][\"name\"] == bt[\"function\"][\"name\"]:\n                    res = bt[\"run\"](**{**json.loads(tool[\"function\"][\"arguments\"]), **bt[\"extra\"]})\n                    resolved_responses.append({\n                        \"name\": tool[\"function\"][\"name\"],\n                        \"role\": \"tool\",\n                        \"content\": json.dumps(res),\n                        \"tool_call_id\": \"chatcmpl-\" + tool_calls_result_id,\n                    })\n\n        return unresolved_tool_calls, resolved_responses\n\n    async def handle1(self, context: Context):\n        body = context.body\n        if context.is_tool_call:\n            # Step 1: Prepare the the history of conversation.\n            messages = [\n                f\"<{m['role'].lower()}>\\n{m['content']}\\n</{m['role'].lower()}>\"\n                for m in context.messages\n                if m[\"role\"] != \"system\"\n            ]\n            messages_flatten = \"\\n\".join(messages)\n            \n\n            # Step 2: Prepare tools details and detect the mode of operation.\n            tool_choice = context.params.get(\"tool_choice\", \"auto\")\n            forced_mode = type(tool_choice) == dict and tool_choice.get(\"type\", None) == \"function\"\n\n            if forced_mode:\n                tool_choice = tool_choice[\"function\"][\"name\"]\n                tools_json = json.dumps([t[\"function\"] for t in context.tools if t[\"function\"][\"name\"] == tool_choice], indent=4)\n                system_message = ENFORCED_SYSTAME_MESSAE\n                suffix = get_forced_tool_suffix(tool_choice)\n            else:\n                tool_choice = \"auto\"\n                tools_json = json.dumps([t[\"function\"] for t in context.tools], indent=4)\n                system_message = SYSTEM_MESSAGE\n                suffix = SUFFIX\n\n            # Step 3: Prepare the messages for the model.\n            new_messages = [\n                {\"role\": \"system\", \"content\": system_message},\n                {\n                    \"role\": \"user\",\n                    \"content\": f\"# Conversation History:\\n{messages_flatten}\\n\\n# Available Tools: \\n{tools_json}\\n\\n{suffix}\\n{context.messages[-1]['content']}\",\n                },\n            ]\n\n            # Step 4: Detect the tool calls.\n            tool_calls_result = await self.process_tool_calls(context, new_messages)\n            tool_calls = tool_calls_result[\"tool_calls\"]\n\n            \n            # Step 5: Handle the situation where no tool calls are detected.\n            if not tool_calls:\n                if context.no_tool_behaviour == \"forward\":\n                    context.tools = None\n                    return await super().handle(context)\n                else:\n                    context.response = {\"tool_calls\": []}\n                    tool_response = get_tool_call_response(tool_calls_result, [], [])\n                    missed_tool_logger.debug(\n                        f\"Last message content: {context.last_message['content']}\"\n                    )\n                    return JSONResponse(content=tool_response, status_code=200)\n\n            \n            # Step 6: Process built-in toola and resolve the tool calls, here on the server. In case there is unresolved tool calls, we will return the tool calls to the client to resolve them. But if all tool calls are resolved, we will continue to the next handler.\n            unresolved_tol_calls = [\n                t\n                for t in tool_calls\n                if t[\"function\"][\"name\"] not in context.builtin_tool_names\n            ]\n            resolved_responses = []\n            for tool in tool_calls:\n                for bt in context.builtin_tools:\n                    if tool[\"function\"][\"name\"] == bt[\"function\"][\"name\"]:\n                        res = bt[\"run\"](\n                            **{\n                                **json.loads(tool[\"function\"][\"arguments\"]),\n                                **bt[\"extra\"],\n                            }\n                        )\n                        resolved_responses.append(\n                            {\n                                \"name\": tool[\"function\"][\"name\"],\n                                \"role\": \"tool\",\n                                \"content\": json.dumps(res),\n                                \"tool_call_id\": \"chatcmpl-\" + tool_calls_result.id,\n                            }\n                        )\n\n                if not unresolved_tol_calls:\n                    context.messages.extend(resolved_responses)\n                    return await super().handle(context)\n\n            # Step 7: If reach here, it means there are unresolved tool calls. We will return the tool calls to the client to resolve them.\n            tool_response = get_tool_call_response(\n                tool_calls_result, unresolved_tol_calls, resolved_responses\n            )\n\n            context.response = tool_response\n            return JSONResponse(content=context.response, status_code=200)\n\n        return await super().handle(context)\n\n    async def process_tool_calls(self, context, new_messages):\n        try:\n            evaluation_cycles_count = EVALUATION_CYCLES_COUNT\n\n            def call_route(messages):\n                completion = context.client.route(\n                    model=context.client.parser_model,\n                    messages=messages,\n                    temperature=0,\n                    max_tokens=512,\n                    top_p=1,\n                    stream=False,\n                )\n\n                response = completion.choices[0].message.content\n                response = response.replace(\"\\_\", \"_\")\n                if TOOLS_OPEN_TOKEN in response:\n                    response = response.split(TOOLS_OPEN_TOKEN)[1].split(\n                        TOOLS_CLOSE_TOKEN\n                    )[0]\n                if \"```json\" in response:\n                    response = response.split(\"```json\")[1].split(\"```\")[0]\n\n                try:\n                    tool_response = json.loads(response)\n                    if isinstance(tool_response, list):\n                        tool_response = {\"tool_calls\": tool_response}\n                    # Check all detected functions exist in the available tools\n                    valid_names = [t['function'][\"name\"] for t in context.tools]\n                    available_tools = [t for t in tool_response.get(\"tool_calls\", []) if t['name'] in valid_names]\n                    tool_response = {\n                        \"tool_calls\": available_tools,\n                    }\n                    # tool_response = {\"tool_calls\": []}\n                        \n                    \n                    return tool_response.get(\"tool_calls\", []), completion\n                except json.JSONDecodeError as e:\n                    print(f\"Error parsing the tool response: {e}\")\n                    return [], None\n\n            with concurrent.futures.ThreadPoolExecutor() as executor:\n                futures = [\n                    executor.submit(call_route, new_messages)\n                    for _ in range(evaluation_cycles_count)\n                ]\n                results = [\n                    future.result()\n                    for future in concurrent.futures.as_completed(futures)\n                ]\n\n            tool_calls_list, completions = zip(*results)\n\n            tool_calls_count = defaultdict(int)\n            for tool_calls in tool_calls_list:\n                for func in tool_calls:\n                    tool_calls_count[func[\"name\"]] += 1\n\n            pickup_threshold = math.floor(evaluation_cycles_count * 0.7)\n            final_tool_calls = []\n            for tool_calls in tool_calls_list:\n                for func in tool_calls:\n                    if tool_calls_count[func[\"name\"]] >= pickup_threshold:\n                        # ppend if function is not already in the list\n                        if not any(\n                            f['function'][\"name\"] == func[\"name\"] for f in final_tool_calls\n                        ):\n                            final_tool_calls.append(\n                                {\n                                    \"id\": f\"call_{func['name']}_{str(uuid.uuid4())}\",\n                                    \"type\": \"function\",\n                                    \"function\": {\n                                        \"name\": func[\"name\"],\n                                        \"arguments\": json.dumps(func[\"arguments\"]),\n                                    },\n                                }\n                            )\n\n            total_prompt_tokens = sum(c.usage.prompt_tokens for c in completions if c)\n            total_completion_tokens = sum(\n                c.usage.completion_tokens for c in completions if c\n            )\n            total_tokens = sum(c.usage.total_tokens for c in completions if c)\n\n            last_completion = completions[-1] if completions else None\n\n            return {\n                \"tool_calls\": final_tool_calls,\n                \"last_completion\": last_completion,\n                \"usage\": {\n                    \"prompt_tokens\": total_prompt_tokens,\n                    \"completion_tokens\": total_completion_tokens,\n                    \"total_tokens\": total_tokens,\n                },\n            }\n\n        except Exception as e:\n            print(f\"Error processing the tool calls: {e}\")\n            raise e\n\n\nclass ToolResponseHandler(Handler):\n    async def handle(self, context: Context):\n        body = context.body\n        if context.is_tool_response:\n            messages = context.messages\n\n            for message in messages:\n                if message[\"role\"] == \"tool\":\n                    message[\"role\"] = \"user\"\n                    message[\"content\"] = get_func_result_guide(message[\"content\"])\n\n            try:\n                params = {\n                    \"temperature\": 0.5,\n                    \"max_tokens\": 1024,\n                }\n                params = {**params, **context.params}\n\n                completion = context.client.route(\n                    messages=messages,\n                    **context.client.clean_params(params),\n                )\n                context.response = completion.model_dump()\n                return JSONResponse(content=context.response, status_code=200)\n            except Exception as e:\n                raise e\n\n        return await super().handle(context)\n"
  },
  {
    "path": "app/libs/vision_handler.py",
    "content": "from prompts import *\nfrom utils import describe\nfrom .context import Context\nfrom .base_handler import Handler\n\n\nclass ImageMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    prompt = None\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            # new_messages.append({\"role\": message[\"role\"], \"content\": content[\"text\"]})\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                prompt = prompt or IMAGE_DESCRIPTO_PROMPT\n                                description = describe(prompt, image_url)\n                                if description:\n                                    description = get_image_desc_guide(image_ref, description)\n                                    new_messages.append(\n                                        {\"role\": message[\"role\"], \"content\": description}\n                                    )\n                                    image_ref += 1\n                                else:\n                                    pass\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n                else:\n                    new_messages.append(message)\n            else:\n                new_messages.append(message)\n\n        context.messages = new_messages\n        return await super().handle(context)\n    \n\nclass ImageLLavaMessageHandler(Handler):\n    async def handle(self, context: Context):\n        new_messages = []\n        image_ref = 1\n        for message in context.messages:\n            new_messages.append(message)\n            if message[\"role\"] == \"user\":\n                if isinstance(message[\"content\"], list):\n                    for content in message[\"content\"]:\n                        if content[\"type\"] == \"text\":\n                            prompt = content[\"text\"]\n                        elif content[\"type\"] == \"image_url\":\n                            image_url = content[\"image_url\"][\"url\"]\n                            try:\n                                description = describe(prompt, image_url)\n                                new_messages.append(\n                                    {\"role\": \"assistant\", \"content\": description}\n                                )\n                                image_ref += 1\n                            except Exception as e:\n                                print(f\"Error describing image: {e}\")\n                                continue\n        context.messages = new_messages\n        return await super().handle(context)\n\n"
  },
  {
    "path": "app/main.py",
    "content": "from fastapi import FastAPI\nfrom fastapi.responses import HTMLResponse\nfrom fastapi.templating import Jinja2Templates\nfrom fastapi.staticfiles import StaticFiles\nfrom starlette.middleware.cors import CORSMiddleware\nfrom starlette.requests import Request\nfrom routes import proxy\nfrom routes import examples\nfrom utils import create_logger\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\napp = FastAPI()\n\nlogger = create_logger(\"app\", \".logs/access.log\")\napp.mount(\"/static\", StaticFiles(directory=\"frontend/assets\"), name=\"static\")\ntemplates = Jinja2Templates(directory=\"frontend/pages\")\n\n\norigins = [\n    \"*\",\n]\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=origins,\n    allow_credentials=True,\n    allow_methods=[\"*\"],\n    allow_headers=[\"*\"],\n)\n\n\n@app.middleware(\"http\")\nasync def log_requests(request: Request, call_next):\n    if \"/proxy\" in request.url.path:\n        client_ip = request.client.host\n        logger.info(\n            f\"Incoming request from {client_ip}: {request.method} {request.url}\"\n        )\n        response = await call_next(request)\n        # logger.info(f\"Response status code: {response.status_code}\")\n        return response\n    else:\n        return await call_next(request)\n\n\napp.include_router(proxy.router, prefix=\"/proxy\")\napp.include_router(examples.router, prefix=\"/examples\")\n\n\n@app.get(\"/\", response_class=HTMLResponse)\nasync def index(request: Request):\n    return templates.TemplateResponse(\"index.html\", {\"request\": request})\n\n\n# Add an get endpoint simple return the evrsion of the app\n@app.get(\"/version\")\nasync def version():\n    return {\"version\": \"0.0.5\"}\n\n\nif __name__ == \"__main__\":\n    import uvicorn\n\n    # uvicorn.run(\"main:app\", host=os.getenv(\"HOST\"), port=int(os.getenv('PORT')), workers=1, reload=True)\n    uvicorn.run(\n        \"main:app\", host=os.getenv(\"HOST\"), port=int(os.getenv(\"PORT\")), workers=1, reload=False\n    )\n"
  },
  {
    "path": "app/models.py",
    "content": "# To be developed"
  },
  {
    "path": "app/prompts.py",
    "content": "SYSTEM_MESSAGE_v0 = \"\"\"You are a functiona-call proxy for an advanced LLM. Your jobe is to identify the required tools for answering the user queries, if any. You will received the result of those tools, and then based ont hem you generate final response for user. Some of these tools are like \"send_email\", \"run python code\" and etc. Remember you are not in charge to execute these tools as you ar  an AI model, you just detect them, then the middle system, executes, and returns you with response, then you use it to generate final response.\n\nA history of conversations between an AI assistant and the user, plus the last user's message, is given to you.\n\nIn addition, you have access to a list of available tools. Each tool is a function that requires a set of parameters and, in response, returns information that the AI assistant needs to provide a proper answer.\n\nThe list of tools is a JSON list, with each tool having a name, a description to help you identify which tool might be needed, and \"parameters,\" which is a JSON schema to explain what parameters the tool needs, and you have to extract their value from the user's last message.\n\nDepending on the user's question, the AI assistant can either directly answer the user's question without using a tool, or it may need to first call one or multiple tools, wait for the answer, then aggregate all the answers and provide the final answer to the user's last questions.\n\nYour job is to closely check the user's last message and the history of the conversation, then decide if the AI assistant needs to answer the question using any tools. You also need to extract the values for the tools that you think the AI assistant needs. Remember you can select multiple tools if needed.\n\nNotes:\n- If you can synthesis the answer without using any tools, then return an empty list for \"tool_calls\".\n- You need tools if there is clear direction between the user's last message and the tools description.\n- If you can't devise a value for a parameter directly from the user's message, only return null and NEVER TRY TO GUESS THE VALUE.\n- You do NOT need to remind user that you are an AI model and can not execute any of the tools, NEVER mention this, and everyone is aware of that.\n\nMESSAGE SUFFIX: \n- \"SYSTEM MESSGAE\": Whenever a message starts with 'SYSTEM MESSAGE', that is a guide and help information for you to generate your next response. Do not consider them a message from the user, and do not reply to them at all. Just use the information and continue your conversation with the user.\n- \"IMAGE [ref_index]\": Whenever a message starts with 'IMAGE', that is a description of an images uploaded bu user. This information you can use it to generate your next responses, in case user refers to the image. Do not consider them a message from the user, and do not reply to them at all. Just use the information and continue your conversation with the user. The [ref_index] is the index of the image in the list of images uploaded by the user. \"\"\"\n\nSYSTEM_MESSAGE = \"\"\"You are a function-call proxy for an advanced LLM. Your job is to identify the required tools for answering the user queries, if any. You will received the result of those tools, and then based on them you select the tool(s) must be called to prepare response for user. You alwayse return JSON. generate final JSON response for user. \n\nA history of conversations between an AI assistant and the user, plus the last user's message, is given to you.\n\nIn addition, you have access to a list of available tools. Each tool is a function that requires a set of parameters and, in response, returns information that the AI assistant needs to provide a proper answer.\n\nThe list of tools is a JSON list, with each tool having a name, a description to help you identify which tool might be needed, and \"parameters,\" which is a JSON schema to explain what parameters the tool needs, and you have to extract their value from the user's last message.\n\nDepending on the user's question, the AI assistant can either directly answer the user's question without using a tool, or it may need to first call one or multiple tools, wait for the answer, then aggregate all the answers and provide the final answer to the user's last questions.\n\nYour job is to closely check the user's last message and the history of the conversation, then decide if the AI assistant needs to answer the question using any tools. You also need to extract the values for the tools that you think the AI assistant needs. Remember you can select multiple tools if needed.\n\nNotes:\n- If you can synthesis the answer without using any tools, then return an empty list for \"tool_calls\".\n- You need tools if there is clear direction between the user's last message and the tools description.\n- If you can't devise a value for a parameter directly from the user's message, only return null and NEVER TRY TO GUESS THE VALUE.\n- You do NOT need to remind user that you are an AI model and can not execute any of the tools, NEVER mention this, and everyone is aware of that.\n\nMESSAGE SUFFIX: \n- \"FUNCTION RESPONSE\": This is the response of a function call, which you requested and system provide it back to you. Do not consider them as user message and do not reply to them at all. Just use the information and continue your conversation with the user.\n- \"IMAGE [ref_index]\": Whenever a message starts with 'IMAGE', that is a description of an images uploaded bu user. This information you can use it to generate your next responses, in case user refers to the image. Do not consider them a message from the user, and do not reply to them at all. Just use the information and continue your conversation with the user. The [ref_index] is the index of the image in the list of images uploaded by the user. \"\"\"\n\n\nENFORCED_SYSTAME_MESSAE = \"\"\"A history of conversations between an AI assistant and the user, plus the last user's message, is given to you.\n\nYou have access to a specific tool that the AI assistant must use to provide a proper answer. The tool is a function that requires a set of parameters, which are provided in a JSON schema to explain what parameters the tool needs. Your task is to extract the values for these parameters from the user's last message and the conversation history.\n\nYour job is to closely examine the user's last message and the history of the conversation, then extract the necessary parameter values for the given tool based on the provided JSON schema. Remember that you must use the specified tool to generate the response.\n\nYou should think step by step, provide your reasoning for your response, then add the JSON response at the end following the below schema:\n\n\n{\n    \"tool_calls\": [{\n        \"name\": \"function_name\",\n        \"arguments\": {\n            \"arg1\": \"value1\",\n            \"arg2\": \"value2\",\n            ...\n        }]\n    }\n}\n\n\n**Wrap the JSON response between ```json and ```, and rememebr \"tool_calls\" is a list.**. \"\"\"\n\n\nCLEAN_UP_MESSAGE = \"When I tried to extract the content between ```json and ``` and parse the content to valid JSON object, I faced with the abovr error. Remember, you are supposed to wrap the schema between ```json and ```, and do this only one time. First find out what went wrong, that I couldn't extract the JSON between ```json and ```, and also faced error when trying to parse it, then regenerate the your last message and fix the issue.\"\n\nSUFFIX = \"\"\"# Example of your response:\n<justification>\nHere you explain why you think a tool or multiple tools are needed and how you extracted the values for the parameters from the user's last message and the conversation history. Also, you may explain why there is no need for any tools.\n</justification>\n\n<selected_tools> \n{\n    \"tool_calls\" : [\n        { \n            \"name\": \"function_name_1\",\n            \"arguments\": {\n                \"arg1\" : \"value1\", \"arg2\": \"value2\", ...\n            }\n        },\n        { \n            \"name\": \"function_name_2\",\n            \"arguments\": {\n                \"arg1\" : \"value1\", \"arg2\": \"value2\", ...\n            }\n        }, ...\n    ]\n}\n</selected_tools>\n\n**If there is no need for any tools, then return an empty list for \"tool_calls\", like \"{ \"tool_calls\": [] }\".**\n\n# Task:\nThink step by step and justify your response in only two sentences. \n\nRemember: \n- You may select multiple tools if needed.\n- **IF for some arguments there is no direct and clear value based ont he history of conversation and user last message then assign null to them. Therefore NEVER TRY TO GUESS THE VALUE.**\n- **ONLY USE THE MENTIONED TOOLS ABOVE AND NOTHING OUT OF THAT. Do not suggest a function that is not in the list of tools.**\n- BE CONCISE AND TO THE POINT. DO NOT ADD ANY UNNECESSARY INFORMATION. MAKER YOUR JUSTIFICATION SHORT.\n- **Dont forget to refer to the histocry of conversation, when you are trying to figure out values of arguments for tool(s) you picked up.**\n- **Some time user may have to refer to the previous messages, to extract the proepr value fo ruser arguments, becuase user may refer to them in his/her last message.**\n- **Use the user's last message to detect the tool(s) you need to call, and use the history of conversation to extract the values of arguments for the tool(s) you picked up.**\n\nIMPORTANT: Not every situiation need a tools, so don't force it, if the question doesn't match with any of tools simply retirn an empty list for \"tool_calls\" and justify your response.\n\n\n# FEW SHOTS: Here we provide some example for you to learn how to generate your response.\n<few_shots>\nFEW_SHOTS\n</few_shots>\n\nMake decision based on on the last user message:\"\"\"\n\nENFORCE_SUFFIX = \"\"\"# Example of your response:\n<selected_tool_arguments_data> \n{\n    \"tool_calls\" : [{\n        \"name\": \"function_name\",\n        \"arguments\": {\n            \"arg1\": \"value1\",\n            \"arg2\": \"value2\",\n            ...\n        }]\n}\n</selected_tool_arguments_data>\n\nNOTE:\n- **IF for some arguments there is no direct and clear value based ont he history of conversation and user last message then assign null to them. Therefore NEVER TRY TO GUESS THE VALUE.**\n- **ONLY USE THE MENTIONED TOOLS ABOVE AND NOTHING OUT OF THAT. Do not suggest a function that is not in the list of tools.**\n- BE CONCISE AND TO THE POINT. DO NOT ADD ANY UNNECESSARY INFORMATION. MAKER YOUR JUSTIFICATION SHORT.\n- **Dont forget to refer to histocy of conversation, when you are trying to figure out values of arguments for the given tool (function).**\n- **Some time user may have to refer to the previous messages so you can find the argument value from there.**\n\nNow extract required data for this tool argument, if any. Make your decision based on on the last user's message:\"\"\"\n\n\nTOOLS_OPEN_TOKEN = \"<selected_tools>\"\nTOOLS_CLOSE_TOKEN = \"</selected_tools>\"\n\n\nFORCE_CALL_SUFFIX = \"\"\"For this task, you HAVE to choose the tool (function) {tool_name}, and ignore other rools. Therefore think step by step and justify your response, then closely examine the user's last message and the history of the conversation, then extract the necessary parameter values for the given tool based on the provided JSON schema. Remember that you must use the specified tool to generate the response. Finally generate a JSON response wrapped between \"<selected_tools>\" and \"</selected_tools>\". Remember to USE THIS JSON WRAPPER ONLY ONE TIME.\"\"\"\n\n\nIMAGE_DESCRIPTO_PROMPT = \"\"\"The user has uploaded an image. List down in very detail what the image is about. List down all objetcs you see and their description. Your description should be enough for a blind person to be able to visualize the image and answe ny question about it.\"\"\"\n\n\ndef get_forced_tool_suffix(tool_name: str) -> str:\n    return FORCE_CALL_SUFFIX.format(tool_name=tool_name)\n\n\ndef get_func_result_guide(function_call_result: str) -> str:\n    return f\"SYSTEM MESSAGE: \\n```json\\n{function_call_result}\\n```\\n\\nThe above is the result after functions are called. Use the result to answer the user's last question.\\n\\n\"\n\n\ndef get_image_desc_guide(ref_index: int, description: str) -> str:\n    return f\"IMAGE: [{ref_index}] : {description}.\\n\\n\"\n\n\nFEW_SHOTS = [\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nI'm planning a birthday party for my friend. Can you suggest some good catering options?\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_catering_options\",\n        \"description\": \"Use this function to retrieve a list of available catering options based on the type of event and dietary preferences.\\n\\n:param event_type: The type of event (e.g., birthday, wedding, corporate).\\n:param dietary_preferences: Any specific dietary preferences or restrictions (e.g., vegetarian, gluten-free).\\n:return: A string containing the list of catering options matching the provided criteria.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"event_type\": {\n                    \"type\": \"string\"\n                },\n                \"dietary_preferences\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"book_catering\",\n        \"description\": \"Use this function to book a catering service when all required information is available (selected catering option, event details, and payment information).\\n\\nArgs:\\n    selected_option (str): The selected catering option.\\n    event_details (dict): The details of the event (date, time, location, number of guests).\\n    payment_info (dict): The payment information provided by the user.\\n\\nReturns:\\n    The result of the booking operation.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"selected_option\": {\n                    \"type\": \"string\"\n                },\n                \"event_details\": {\n                    \"type\": \"object\"\n                },\n                \"payment_info\": {\n                    \"type\": \"object\"\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user is asking for suggestions on catering options for a birthday party. We can use the \"get_catering_options\" tool to retrieve a list of suitable options based on the event type (birthday) and any dietary preferences provided by the user.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": [\n        {\n            \"name\": \"get_catering_options\",\n            \"arguments\": {\n                \"event_type\": \"birthday\",\n                \"dietary_preferences\": \"\"\n            }\n        }\n    ]\n}\n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nI'm looking for a new laptop for work. Any recommendations?\n</user>\n<assistant>\nSure, I can help you with that. To provide the best recommendations, could you please share some more information about your requirements? Here are a few questions to consider:\n\n1. What is your budget range?\n2. What size laptop do you prefer (e.g., 13-inch, 15-inch)?\n3. Do you have any specific performance requirements (e.g., processor, RAM, storage)?\n4. Are there any particular features you're looking for (e.g., long battery life, lightweight)?\n\nOnce you provide these details, I can suggest some laptop models that would suit your needs.\n</assistant>\n<user>\nThanks for the questions. My budget is around $1,500, and I prefer a 14-inch or 15-inch laptop. I need a powerful processor and at least 16GB of RAM for running multiple applications simultaneously. A long battery life would be a plus.\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_laptop_recommendations\",\n        \"description\": \"Use this function to retrieve a list of laptop recommendations based on the provided criteria (budget, size, performance requirements, and features).\\n\\n:param criteria: A dictionary containing the user's laptop preferences.\\n:return: A string containing the list of laptop recommendations matching the provided criteria.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"criteria\": {\n                    \"type\": \"object\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"compare_laptops\",\n        \"description\": \"Use this function to compare the specifications and features of two or more laptop models.\\n\\n:param laptop_models: A list of laptop models to compare.\\n:return: A string containing the comparison of the provided laptop models.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"laptop_models\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"string\"\n                    }\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user has provided specific criteria for laptop recommendations, including budget, size, performance requirements, and desired features. We can use the \"get_laptop_recommendations\" tool to retrieve a list of laptops matching these criteria. Additionally, we can use the \"compare_laptops\" tool to provide a comparison of the recommended models to help the user make an informed decision.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": [\n        {\n            \"name\": \"get_laptop_recommendations\",\n            \"arguments\": {\n                \"criteria\": {\n                    \"budget\": 1500,\n                    \"size\": \"14-inch or 15-inch\",\n                    \"performance\": {\n                        \"processor\": \"powerful\",\n                        \"RAM\": \"at least 16GB\"\n                    },\n                    \"features\": {\n                        \"battery_life\": \"long\"\n                    }\n                }\n            }\n        },\n        {\n            \"name\": \"compare_laptops\",\n            \"arguments\": {\n                \"laptop_models\": []\n            }\n        }\n    ]\n}\n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nI'm planning a trip to Europe next month. Do you have any tips for packing light?\n</user>\n<assistant>\nPacking light is a great way to make your travel experience more enjoyable and hassle-free. Here are some tips to help you pack efficiently:\n\n1. Choose versatile clothing items that can be mixed and matched to create multiple outfits.\n2. Opt for lightweight, quick-drying fabrics that are easy to wash and don't take up much space.\n3. Limit your shoes to two or three comfortable pairs that can be worn with different outfits.\n4. Use packing cubes or compression bags to organize your belongings and save space in your luggage.\n5. Pack travel-sized toiletries and only bring the essentials.\n6. Wear your bulkiest items, like jackets or boots, on the plane to save space in your luggage.\n7. Consider doing laundry during your trip to reduce the amount of clothing you need to pack.\n\nRemember, the key is to be selective and only pack what you truly need. If you have any specific questions about packing for your European trip, feel free to ask!\n</assistant>\n<user>\nThanks for the great advice! I'll definitely keep these tips in mind while packing. Do you have any recommendations for must-see attractions or hidden gems in Europe?\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_packing_checklist\",\n        \"description\": \"Use this function to generate a personalized packing checklist based on the user's destination, trip duration, and travel preferences.\\n\\n:param destination: The user's travel destination.\\n:param duration: The duration of the user's trip.\\n:param preferences: Any specific travel preferences or requirements.\\n:return: A string containing the personalized packing checklist.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"destination\": {\n                    \"type\": \"string\"\n                },\n                \"duration\": {\n                    \"type\": \"string\"\n                },\n                \"preferences\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user is asking for recommendations on must-see attractions and hidden gems in Europe, which is a broad and open-ended question. The available tool, \"get_packing_checklist,\" is not relevant to answering this query. The best approach would be to provide a general response based on popular destinations and experiences, or ask the user for more specific information about their interests and the countries they plan to visit.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": []\n}\n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nHi what are available comic movies on 23th March?\n</user>\n<assistant>\nThank you for providing the information! I found two comic movies showing on March 23rd. Here are the details:\n\n1. \"Never Say Never Again\" - High octane thrillers, PG, 2 hrs 14 mins, 1983, HD.\n   Airing time: 2:25 PM EDT.\n\n2. \"Scary Movie 3\" - Slashers, PG-13, 1 hr 25 mins, 2003, HD.\n   Airing time: 11:23 PM EDT.\n\nAdditionally, I noticed \"The Strangers: Prey at Night\" is also available on March 23rd if you're interested in a horror movie.\n- Horror, R, 1 hr 25 mins, 2018, HD.\n  Airing time: 12:03 PM EDT.\n\nPlease let me know if you'd like to book any of these movies. I'm here to help!\n</assistant>\n<user>\nInteresting, tell me more about this movie \"Never Say Never Again,\", what is its plot?\n</user>\n\n# Available Tools: \n[\n    {\n        \"name\": \"get_available_slots\",\n        \"description\": \"Use this function to search the Cinemax database of available movies, show time, and date.\\n\\n:param query: The query to search the Cinemax database of available movies, show time, and date.\\n:return: A string containing the response to the query.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"movie_slot_query\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"book_cinema_ticket\",\n        \"description\": \"Use this function ONLY for booking a ticket, when all info is available (movie name, date, time and suer email). Do NOT use this function when user asks for movie details and other things\\n\\nArgs:\\n    movie_name (str): The name of the movie.\\n    date (Optional[str], optional): The date of the movie.\\n    time (Optional[str], optional): The time of the movie.\\n    user_email (Optional[str], optional): The email of the user. Defaults to None.\\n\\nReturns:\\n    The result of the operation.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"movie_name\": {\n                    \"type\": \"string\"\n                },\n                \"date\": {\n                    \"type\": [\n                        \"string\",\n                        \"null\"\n                    ]\n                },\n                \"time\": {\n                    \"type\": [\n                        \"string\",\n                        \"null\"\n                    ]\n                },\n                \"user_email\": {\n                    \"type\": [\n                        \"string\",\n                        \"null\"\n                    ]\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"get_chat_history\",\n        \"description\": \"Returns the chat history between the user and assistant.\\n\\n:param num_chats: The number of chats to return.\\n    Each chat contains 2 messages. One from the user and one from the assistant.\\n    Default: 3\\n:return: A list of dictionaries representing the chat history.\\n\\nExample:\\n    - To get the last chat, use num_chats=1.\\n    - To get the last 5 chats, use num_chats=5.\\n    - To get all chats, use num_chats=None.\\n    - To get the first chat, use num_chats=None and pick the first message.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"num_chats\": {\n                    \"type\": [\n                        \"number\",\n                        \"null\"\n                    ]\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user is asking for more information that non of tools or chat history can provide. Therefore, I can't use any of the tools to answer the user's question. \n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": []\n}   \n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nWhat was the closing price of Apple Inc. (AAPL) stock yesterday?\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_stock_price\",\n        \"description\": \"Use this function to retrieve the latest price information for a given stock symbol.\\n\\n:param symbol: The stock symbol to retrieve the price for (e.g., 'AAPL' for Apple Inc.).\\n:return: A string containing the latest price information for the specified stock.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"get_stock_history\",\n        \"description\": \"Use this function to retrieve the historical price data for a given stock symbol over a specified period.\\n\\n:param symbol: The stock symbol to retrieve the historical data for.\\n:param period: The time period for the historical data (e.g., '1d', '5d', '1mo', '1y').\\n:return: A string containing the historical price data for the specified stock and period.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                },\n                \"period\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user is asking for the closing price of Apple Inc. (AAPL) stock from the previous day. We can use the \"get_stock_price\" tool to retrieve the latest price information for the AAPL stock symbol. The \"get_stock_history\" tool is not needed in this case, as the user is only asking for the closing price from yesterday.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": [\n        {\n            \"name\": \"get_stock_price\",\n            \"arguments\": {\n                \"symbol\": \"AAPL\"\n            }\n        }\n    ]\n}\n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nI'm considering investing in Tesla (TSLA) stock. Can you provide me with some information to help me make a decision?\n</user>\n<assistant>\nTo help you make an informed decision about investing in Tesla (TSLA) stock, I can provide you with the following information:\n\n1. Latest price: The current price of TSLA stock can give you an idea of its market value.\n\n2. Historical performance: Analyzing the stock's price history over different periods (e.g., 1 month, 6 months, 1 year) can help you understand its volatility and potential for growth.\n\n3. Company fundamentals: Researching Tesla's financial statements, revenue growth, profitability, and future projects can provide insights into the company's overall health and potential.\n\n4. Market sentiment: Monitoring news, analyst ratings, and investor sentiment can help gauge the market's perception of Tesla stock.\n\nLet me know which specific information you'd like me to provide first, and I'll be happy to assist you further.\n</assistant>\n<user>\nThanks for the overview. Let's start with the latest price and the historical performance over the past 6 months. That should give me a good starting point.\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_stock_price\",\n        \"description\": \"Use this function to retrieve the latest price information for a given stock symbol.\\n\\n:param symbol: The stock symbol to retrieve the price for (e.g., 'AAPL' for Apple Inc.).\\n:return: A string containing the latest price information for the specified stock.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"get_stock_history\",\n        \"description\": \"Use this function to retrieve the historical price data for a given stock symbol over a specified period.\\n\\n:param symbol: The stock symbol to retrieve the historical data for.\\n:param period: The time period for the historical data (e.g., '1d', '5d', '1mo', '1y').\\n:return: A string containing the historical price data for the specified stock and period.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                },\n                \"period\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user has requested two specific pieces of information to help them make a decision about investing in Tesla (TSLA) stock: the latest price and the historical performance over the past 6 months. To provide this information, we need to use both the \"get_stock_price\" and \"get_stock_history\" tools.\n\n1. \"get_stock_price\" will be used to retrieve the current price of TSLA stock.\n2. \"get_stock_history\" will be used to retrieve the price history of TSLA stock over the past 6 months (period: '6mo').\n\nBy combining the results from these two tools, we can give the user the requested information to support their investment decision.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": [\n        {\n            \"name\": \"get_stock_price\",\n            \"arguments\": {\n                \"symbol\": \"TSLA\"\n            }\n        },\n        {\n            \"name\": \"get_stock_history\",\n            \"arguments\": {\n                \"symbol\": \"TSLA\",\n                \"period\": \"6mo\"\n            }\n        }\n    ]\n}\n</selected_tools>\"\"\",\n    },\n    {\n        \"input\": \"\"\"# Conversation History:\n<user>\nWhat do you think about the recent performance of the stock market?\n</user>\n<assistant>\nThe stock market's recent performance has been mixed, with some sectors showing strong growth while others have experienced volatility. Here are a few key points:\n\n1. Tech stocks: Many technology companies have seen significant gains, driven by strong earnings and optimism about the sector's long-term prospects.\n\n2. Energy sector: Oil and gas stocks have been under pressure due to fluctuating crude oil prices and concerns about the global economic recovery.\n\n3. Health care: The health care sector has been a mixed bag, with some companies benefiting from the focus on COVID-19 treatments and vaccines, while others have faced challenges.\n\n4. Overall market: The broader stock market indices, such as the S&P 500 and Dow Jones Industrial Average, have reached record highs recently, supported by positive economic data and optimism about the post-pandemic recovery.\n\nIt's important to remember that the stock market's performance is influenced by various factors, including economic indicators, geopolitical events, and investor sentiment. As an AI language model, I cannot provide personalized investment advice, but I suggest researching specific sectors and companies, and consulting with a financial advisor before making investment decisions.\n</assistant>\n<user>\nThanks for the insights. It's helpful to get a general overview of the market's performance across different sectors. I'll definitely do more research and consult with a financial advisor before making any investment decisions.\n</user>\n\n# Available Tools:\n[\n    {\n        \"name\": \"get_stock_price\",\n        \"description\": \"Use this function to retrieve the latest price information for a given stock symbol.\\n\\n:param symbol: The stock symbol to retrieve the price for (e.g., 'AAPL' for Apple Inc.).\\n:return: A string containing the latest price information for the specified stock.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    },\n    {\n        \"name\": \"get_stock_history\",\n        \"description\": \"Use this function to retrieve the historical price data for a given stock symbol over a specified period.\\n\\n:param symbol: The stock symbol to retrieve the historical data for.\\n:param period: The time period for the historical data (e.g., '1d', '5d', '1mo', '1y').\\n:return: A string containing the historical price data for the specified stock and period.\",\n        \"parameters\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"symbol\": {\n                    \"type\": \"string\"\n                },\n                \"period\": {\n                    \"type\": \"string\"\n                }\n            }\n        }\n    }\n]\"\"\",\n        \"output\": \"\"\"<justification>\nThe user's question about the recent performance of the stock market is broad and does not require the use of the available tools. The conversation history shows that the assistant has provided a general overview of the market's performance across different sectors, which sufficiently addresses the user's question. The user also acknowledges that the insights are helpful and expresses their intention to conduct further research and consult with a financial advisor. As a result, no specific tools are needed to answer this query.\n</justification>\n\n<selected_tools>\n{\n    \"tool_calls\": []\n}\n</selected_tools>\"\"\",\n    },\n]\n\nimport random\ndef get_suffix():\n    random.shuffle(FEW_SHOTS)\n    # Turn each element of FEW_SHOTS into a string like '-- EXAMPLE i ---\\nINPUT:\\n{input}\\n\\nOUTPUT:\\n{output}\\n\\n', then join them by \\n\\n\n    few_shots = \"\\n\\n\".join(\n        [\n            f'-- EXAMPLE {i} ---\\nINPUT:\\n{example[\"input\"]}\\n\\nOUTPUT:\\n{example[\"output\"]}\\n\\n'\n            for i, example in enumerate(FEW_SHOTS, 1)\n        ]\n    )\n    # Replace FEW_SHOTS with the actual examples\n    return SUFFIX.replace(\"FEW_SHOTS\", few_shots)\n"
  },
  {
    "path": "app/providers.py",
    "content": "from groq import Groq\nfrom openai import OpenAI\nfrom litellm import completion as sync_call_llm\nimport litellm\n\n\nclass BaseProvider:\n    def __init__(self, api_key: str, base_url = None):\n        self.api_key = api_key\n        self.parser_model = \"\"\n        self.route_model = \"\"\n\n    def route(self, model: str, messages: list, **kwargs):\n        pass\n\n    async def route_async(self, model: str, messages: list, **kwargs):\n        pass\n\n    def clean_params(self, params):\n        pass\n\n\nclass OpenaiProvider(BaseProvider):\n    def __init__(self, api_key: str, base_url = None):\n        super().__init__(api_key)\n        self._client = OpenAI(api_key=api_key)\n        self.parser_model = \"gpt-3.5-turbo\"\n        self.route_model = \"gpt-3.5-turbo\"\n        self.exclude_params = [\"messages\"]\n\n    def route(self, model: str, messages: list, **kwargs):\n        completion = self._client.chat.completions.create(\n            model=model,\n            messages=messages,\n            **kwargs\n        )\n        return completion\n\n    def clean_params(self, params):\n        return {k: v for k, v in params.items() if k not in self.exclude_params}\n\n\nclass GroqProvider(BaseProvider):\n    def __init__(self, api_key: str, base_url = None):\n        super().__init__(api_key)\n        self._client = Groq(api_key=api_key)\n        self.parser_model = \"mixtral-8x7b-32768\"\n        self.route_model = \"mixtral-8x7b-32768\"\n        self.exclude_params = [\"messages\", \"tools\", \"tool_choice\"]\n       \n    def route(self, model: str, messages: list, **kwargs):\n        completion =  self._client.chat.completions.create(\n            model=model,\n            messages=messages,\n            **kwargs\n        )\n        return completion\n    \n    def clean_params(self, params):\n        return {k: v for k, v in params.items() if k not in self.exclude_params}\n        \n\nclass OllamaProvider(BaseProvider):\n    def __init__(self, api_key: str, base_url = None):\n        super().__init__(api_key)\n        self.parser_model = \"gemma:2b\"\n        self.route_model = \"gemma:7b\"\n        self.exclude_params = [\"messages\", \"tools\", \"tool_choice\"]\n        \n    def route(self, model: str, messages: list, **kwargs):\n        # Filter out all messages with rol assistant and has key \"tool_calls\"\n        messages = [message for message in messages if message[\"role\"] != \"assistant\" and \"tool_calls\" not in message]\n        params = self.clean_params(kwargs)\n        params = {\n            'max_tokens': 2048,\n            **params\n        }\n        response = sync_call_llm(\n            model=f\"ollama/{model}\",\n            api_base=\"http://localhost:11434\",\n            messages=messages,\n            **self.clean_params(kwargs)\n        )\n\n        return response\n    \n    def clean_params(self, params):\n        return {k: v for k, v in params.items() if k not in self.exclude_params}"
  },
  {
    "path": "app/reasoning/__init__.py",
    "content": ""
  },
  {
    "path": "app/reasoning/base.py",
    "content": "from abc import ABC, abstractmethod\nfrom typing import Dict\n\nclass ReasoningBase(ABC):\n    name: str\n    description: str\n\n    @abstractmethod\n    def run(self, context) -> Dict:\n        pass\n\n"
  },
  {
    "path": "app/reasoning/rerank.py",
    "content": "from pydantic import BaseModel, Field\nfrom typing import Optional, Dict\nfrom .base import ReasoningBase\nfrom pydantic import Field\nfrom typing import Optional\nimport requests\nimport json, os\nfrom providers import GroqProvider\nimport concurrent.futures\nfrom dotenv import load_dotenv\nload_dotenv()\n\ndef get_rerank_prompt(query, responses, top_k):\n    prompt = f\"\"\"You are an AI assistant tasked with evaluating and selecting the best responses to a user's request. The user's request is:\n\n<context>\n{query}\n</context>\n\nHere are the responses generated by different programmers to the user's request:\n\n{responses}\n\n# Task:\nYour task is to evaluate these responses and select the top {top_k} that best address the user's request. Consider factors such as relevance, clarity, and completeness when making your selection.\n\nAfter selecting the top {top_k} responses, generate a final response by merging and summarizing the selected responses. Format your output as follows:\n\n**Make sure to wrap the final answer supposed to be back to user using >>>**\n\n# Example of your response:\nAfter evaluating the responses, I have selected the top {top_k} that best address the user's request. Here they are:\n\n<response index = 1>\nsummary of response 1\n\n<response index = 2>\nsummary of response 2\n\n...\n\n<response index = top_k>\nsummary of response top_k\n\nBased on these top {top_k} responses, I have generated a final response by merging and summarizing them:\n\n>>>\nfinal_response_for_user\n>>>\"\"\"\n\n    return prompt\n\nclass RerankReasoning(ReasoningBase):\n    name = \"rerank\"\n    description = \"Use this reasoning strategy to generate and rerank responses to a user query.\"\n\n    def __init__(self, generator_model: str, reranker_model: str, n: int = 5, top_k: int = 3):\n        self.generator_model = generator_model\n        self.reranker_model = reranker_model\n        self.n = n\n        self.top_k = top_k\n        self.generator_provider = GroqProvider(api_key=os.getenv(\"GROQ_API_KEY\"))\n        self.reranker_provider = GroqProvider(api_key=os.getenv(\"GROQ_API_KEY\"))\n\n    def run(self, context):\n        message_stories = context.messages\n\n        # Generate responses in parallel using a thread pool\n        with concurrent.futures.ThreadPoolExecutor() as executor:\n            response_futures = [executor.submit(self.generator_provider.route, model=self.generator_model, messages=message_stories) for _ in range(self.n)]\n            responses = [future.result().get(\"response\") for future in concurrent.futures.as_completed(response_futures)]\n\n        unique_responses = list(set(responses))\n\n        # Adjust top_k if it's greater than the number of unique responses\n        if self.top_k > len(unique_responses):\n            self.top_k = int(len(unique_responses) * 0.4)\n\n        # Generate prompt for reranking\n        prompt = get_rerank_prompt(\n            query=message_stories[-1].content,\n            responses='\\n\\n'.join([f'<response index={idx + 1}>\\n{response}\\n</response>' for idx, response in enumerate(unique_responses)]),\n            top_k=self.top_k\n        )\n\n        # Rerank responses\n        rerank_completion = self.reranker_provider.route(model=self.reranker_model, messages=[{\"content\": prompt}])\n        reranked_response = rerank_completion.get(\"response\")\n\n        # Extract the final response\n        final_response = reranked_response.split(\">>>\")[1].split(\">>>\")[0].strip()\n\n        # Add the final response as a new message to the context\n        new_message = {\"role\": \"assistant\", \"content\":final_response}\n        context.messages.append(new_message)\n\n        return new_message"
  },
  {
    "path": "app/routes/__init__.py",
    "content": "from .proxy import router as proxy_router\nfrom .examples import router as examples_router\n\n__all__ = [ \"proxy_router\", \"examples_router\" ]\n"
  },
  {
    "path": "app/routes/examples.py",
    "content": "\nfrom fastapi.responses import FileResponse\nfrom fastapi import APIRouter\nimport os\n\nrouter = APIRouter()\n\n# Add endpoint to downl;oad files in the ../examples folder\n@router.get(\"/{file_path}\")\nasync def read_examples(file_path: str):\n    # get parent directory\n    parent = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))\n    file_path = f\"{parent}/examples/{file_path}\"\n    if os.path.exists(file_path):\n        return FileResponse(file_path)\n    else:\n        return {\"error\": \"File not found.\"}\n\n# @router.get(\"/examples\")\n# async def read_root():\n#     return {\"message\": \"Hello World\", \"examples\": [\n#         \"/example/example_1.py\",\n#         \"/example/example_2.py\",\n#         \"/example/example_3.py\",\n#         \"/example/example_4.py\",\n#     ]}"
  },
  {
    "path": "app/routes/proxy.py",
    "content": "from fastapi import APIRouter, Response, Request, Path, Query\nfrom fastapi.responses import JSONResponse\n# from libs.chains import (\n#     Context,\n#     ProviderSelectionHandler,\n#     ImageMessageHandler,\n#     ToolExtractionHandler,\n#     ToolResponseHandler,\n#     DefaultCompletionHandler,\n#     FallbackHandler,\n# )\n\nfrom libs import (\n    Context,\n    ProviderSelectionHandler,\n    ImageMessageHandler,\n    ToolExtractionHandler,\n    ToolResponseHandler,\n    DefaultCompletionHandler,\n    FallbackHandler,\n)\n\n\nfrom typing import Optional\n\nrouter = APIRouter()\n\n\n# Add get endpoint for /openai/v1 and print request body\n@router.get(\"/{provider}/v1\")\nasync def get_openai_v1(\n    response: Response, provider: str = Path(..., title=\"Provider\")\n) -> JSONResponse:\n    return JSONResponse(content={\"message\": f\"GET request to {provider} v1\"})\n\n\n@router.post(\"/groqchain/{provider}/v1/chat/completions\")\nasync def post_groq_chat_completions(\n    request: Request,\n    provider: str = Path(..., title=\"Provider\")\n) -> JSONResponse:\n    # Call the original post_chat_completions method with provider set to \"groq\"\n    return await post_chat_completions(request, provider=\"groq\")\n\n\n@router.post(\"/{provider}/v1/chat/completions\")\nasync def post_chat_completions(\n    request: Request,\n    provider: str = Path(..., title=\"Provider\")\n) -> JSONResponse:\n    try:\n        if not provider:\n            provider = \"openai\"\n\n        if not ProviderSelectionHandler.provider_exists(provider):\n            return JSONResponse(content={\"error\": \"Invalid provider\"}, status_code=400)\n\n        # Extract the API token and body from the request\n        api_token = request.headers.get(\"Authorization\").split(\"Bearer \")[1]\n        body = await request.json()\n\n        # Initialize the context with request details\n        context = Context(request, provider, body)\n        context.api_token = (\n            api_token  # Adding the API token to the context for use in handlers\n        )\n\n        # Initialize and link the handlers\n        provider_selection_handler = ProviderSelectionHandler()\n        image_message_handler = ImageMessageHandler()\n        tool_extraction_handler = ToolExtractionHandler()\n        tool_response_handler = ToolResponseHandler()\n        default_completion_handler = DefaultCompletionHandler()\n        fallback_handler = FallbackHandler()\n\n        # Set up the chain of responsibility\n        chains = [\n            provider_selection_handler,\n            image_message_handler,\n            tool_extraction_handler,\n            tool_response_handler,\n            default_completion_handler,\n            fallback_handler,\n        ]\n        for i in range(len(chains) - 1):\n            chains[i].set_next(chains[i + 1])\n\n        # provider_selection_handler.set_next(tool_extraction_handler).set_next(\n        #     tool_response_handler\n        # ).set_next(default_completion_handler).set_next(fallback_handler)\n\n        # Execute the chain with the initial context\n        response = await provider_selection_handler.handle(context)\n\n        # Return the response generated by the handlers\n        return response\n    except Exception as e:\n        print(f\"Error processing the request: {e}\")\n        return JSONResponse(\n            content={\"error\": \"An unexpected error occurred\"}, status_code=500\n        )\n"
  },
  {
    "path": "app/utils.py",
    "content": "import logging\nimport os\nimport replicate\nimport base64\nfrom io import BytesIO\n\n\n# To be developed\ndef create_logger(logger_name: str, log_path: str = \".logs/access.log\", show_on_shell: bool = False):\n    log_dir = os.path.dirname(log_path)\n    if not os.path.exists(log_dir):\n        os.makedirs(log_dir)\n    logger = logging.getLogger(logger_name)\n    logger.setLevel(logging.DEBUG)\n    file_handler = logging.FileHandler(log_path)\n    file_handler.setLevel(logging.DEBUG)\n    formatter = logging.Formatter(\n        \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n    )\n    file_handler.setFormatter(formatter)\n    logger.addHandler(file_handler)\n    if show_on_shell:\n        stream_handler = logging.StreamHandler()\n        stream_handler.setLevel(logging.DEBUG)\n        shell_formatter = logging.Formatter(\n            \"%(levelname)s (%(name)s)   %(message)s\"\n        )\n        stream_handler.setFormatter(shell_formatter)\n        logger.addHandler(stream_handler)    \n    return logger\n\n\ndef get_tool_call_response(tool_calls_result, unresolved_tol_calls, resolved_responses):\n    last_completion = tool_calls_result[\"last_completion\"]\n    tool_response = {\n        \"id\": \"chatcmpl-\" + last_completion.id if last_completion else None,\n        \"object\": \"chat.completion\",\n        \"created\": last_completion.created if last_completion else None,\n        \"model\": last_completion.model if last_completion else None,\n        \"choices\": [\n            {\n                \"index\": 0,\n                \"message\": {\n                    \"role\": \"assistant\",\n                    \"content\": \"\", # None,\n                    \"tool_calls\": unresolved_tol_calls,\n                },\n                \"logprobs\": None,\n                \"finish_reason\": \"tool_calls\",\n            }\n        ],\n        \"resolved\": resolved_responses,\n        \"usage\": tool_calls_result[\"usage\"],\n        \"system_fingerprint\": last_completion.system_fingerprint if last_completion else None,\n    }\n    return tool_response\n\ndef describe(prompt: str, image_url_or_base64 : str, **kwargs) -> str:\n    logger = create_logger(\"vision\", \".logs/access.log\", True)\n    try:\n        if image_url_or_base64.startswith(\"data:image/\"):\n            # If the input is a base64 string\n            image_data = base64.b64decode(image_url_or_base64.split(\",\")[1])\n            image_file = BytesIO(image_data)\n        else:\n            # If the input is a URL\n            image_file = image_url_or_base64\n        \n        model_params = {\n            \"top_p\": 1,\n            \"max_tokens\": 1024,\n            \"temperature\": 0.2\n        }\n        model_params.update(kwargs)\n\n        logger.info(\"Running the model\")\n        output = replicate.run(\n            \"yorickvp/llava-13b:01359160a4cff57c6b7d4dc625d0019d390c7c46f553714069f114b392f4a726\",\n            input={\n                \"image\": image_file,\n                \"prompt\": prompt, #\"Describe the image in detail.\",\n                **model_params\n            }\n        )\n        \n        description = \"\"\n        for item in output:\n            if not description:\n                logger.info(\"Streaming...\")\n            description += item\n        \n        return description.strip()\n    except Exception as e:\n        logger.error( f\"Vision model, An error occurred: {e}\")\n    return None\n\n\n\n    # describe(\"Describe the image in detail.\", \"https://replicate.delivery/pbxt/KRULC43USWlEx4ZNkXltJqvYaHpEx2uJ4IyUQPRPwYb8SzPf/view.jpg\")"
  },
  {
    "path": "cookbook/ai_assistant_custome_tools.py",
    "content": "\nimport os, json\nfrom typing import Optional, List\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.knowledge.json import JSONKnowledgeBase\nfrom phi.vectordb.pgvector import PgVector2\nfrom phi.storage.assistant.postgres import PgAssistantStorage\nfrom phi.tools import Toolkit\nfrom phi.tools.email import EmailTools\nfrom phi.utils.log import logger\nfrom phi.tools.email import EmailTools\nfrom phi.knowledge.base import AssistantKnowledge\nfrom phi.knowledge.base import Document\nfrom resources import vector_db\nfrom rich.prompt import Prompt\nfrom dotenv import load_dotenv\nload_dotenv()\n\n# To run this example, first make sure to follow the instructions below:\n# 1. Install the phidata: pip install phidata\n# 2. Run the following command to start a docker, with pgvector db running: phi start resources.py \n# 3. Download the sample of JSON knowledge base from the same folder of this file: cinemax.json\n\nclass CinemaSerachDB(Toolkit):\n    def __init__(\n        self,\n        knowledge_base  : Optional[AssistantKnowledge] = None,\n        num_documents: int = None\n    ):\n        super().__init__(name=\"get_available_slots\")\n        self.knowledge_base = knowledge_base\n        self.num_documents = num_documents\n        self.register(self.get_available_slots)\n\n    def get_available_slots(self, movie_slot_query: str ) -> str:\n        \"\"\"Use this function to search the Cinemax database of available movies, show time, and date.\n\n        :param query: The query to search the Cinemax database of available movies, show time, and date.\n        :return: A string containing the response to the query.\n        \"\"\"\n        relevant_docs: List[Document] = self.knowledge_base.search(query=movie_slot_query, num_documents=self.num_documents)\n        if len(relevant_docs) == 0:\n            return None\n\n        return json.dumps([doc.to_dict() for doc in relevant_docs], indent=2)\n\n\n\nclass CinemaTools(Toolkit):\n    def __init__(\n        self,\n        email_tools: Optional[\"EmailTools\"] = None,\n    ):\n        super().__init__(name=\"cinema_tools\")\n        self.email_tools = email_tools\n        self.register(self.book_cinema_ticket)\n\n    def book_cinema_ticket(self, movie_name: str, date: Optional[str] = None, time: Optional[str] = None, user_email: Optional[str] = None) -> str:\n        \"\"\"Use this function ONLY for booking a ticket, when all info is available (movie name, date, time and suer email). Do NOT use this function when user asks for movie details and other things\n\n        Args:\n            movie_name (str): The name of the movie.\n            date (Optional[str], optional): The date of the movie.\n            time (Optional[str], optional): The time of the movie.\n            user_email (Optional[str], optional): The email of the user. Defaults to None.\n\n        Returns:\n            The result of the operation.\n\n        \"\"\"\n\n        anything_missed = any([not movie_name, not date, not time, not user_email])\n\n        missed_items = []\n\n        if anything_missed:\n            if not date:\n                missed_items.append( \"error: No date provided, I need a date to book a ticket\")\n            \n            if not time:\n                missed_items.append( \"error: No time provided, I need a time to book a ticket\")\n            \n            if not user_email:\n                missed_items.append( \"error: No user email provided, I need an email to send the ticket\")\n\n            missed_itemes = \", \".join(missed_items)\n            return f\"There are some missing items: \\n{missed_itemes}\"\n\n        # Simulate booking the ticket\n        ticket_number = self._generate_ticket_number()\n        logger.info(f\"Booking ticket for {movie_name} on {date} at {time}\")\n\n        # Prepare the email subject and body\n        subject = f\"Your ticket for {movie_name}\"\n        body = f\"Dear user,\\n\\nYour ticket for {movie_name} on {date} at {time} has been booked.\\n\\n\" \\\n               f\"Your ticket number is: {ticket_number}\\n\\nEnjoy the movie!\\n\\nBest regards,\\nThe Cinema Team\"\n\n        # Send the email using the EmailTools\n        if not self.email_tools:\n            return \"error: No email tools provided\"\n        self.email_tools.receiver_email = user_email\n        result = self.email_tools.email_user(subject, body)\n\n        if result.startswith(\"error\"):\n            logger.error(f\"Error booking ticket: {result}\")\n            return result\n        return \"success\"\n\n    def _generate_ticket_number(self) -> str:\n        \"\"\"Generates a dummy ticket number.\"\"\"\n        import random\n        import string\n        return \"\".join(random.choices(string.ascii_uppercase + string.digits, k=10))\n\nkb = JSONKnowledgeBase(\n    path=\"cinemax.json\",\n    vector_db=PgVector2(collection=\"cinemax\", db_url=vector_db.get_db_connection_local()),\n)\nstorage = PgAssistantStorage(\n    table_name=\"cinemax_assistant_storage\",\n    db_url=vector_db.get_db_connection_local(),\n)\n\nmy_groq = OpenAILike(\n        model=\"mixtral-8x7b-32768\",\n        api_key=os.environ[\"GROQ_API_KEY\"],\n        base_url=\"http://localhost:8000/proxy/groq/v1\"\n        # base_url=\"http://groqcall.ai/proxy/groq/v1\"\n    )\n\n\ndef cinemax_assistant(new: bool = False, user: str = \"user\"):\n    run_id: Optional[str] = None\n    # new = False\n    # new = True\n    user_id = user\n\n    if not new:\n        existing_run_ids: List[str] = storage.get_all_run_ids(user_id)\n        if len(existing_run_ids) > 0:\n            run_id = existing_run_ids[0]\n\n    assistant = Assistant(\n        run_id=run_id,\n        user_id=\"test_user\",\n        llm=my_groq,\n        # knowledge_base=kb, Since I created my own tool CinemaSerachDB\n        storage=storage,\n        use_tools=True,\n        tools=[\n            CinemaSerachDB(knowledge_base=kb, num_documents=5),\n            CinemaTools(EmailTools(\"YOUR_EMAIL_ADDRESS\", \"SENDER_NAME\", \"SENDER_EMAIL\", os.environ['email_pass_key'] ))\n        ], show_tool_calls=True, markdown=True,\n        add_chat_history_to_messages=True, # Inject history in every prompt\n        add_datetime_to_instructions=True,\n        limit_tool_access=True,\n        extra_instructions=\"\",\n        output_model= None, # Pydantic\n        prevent_prompt_injection=True,\n        add_references_to_prompt=False, # It searches in the kb and add it to every prompt\n        # add_references_to_prompt=True,\n        add_to_system_prompt=\"\\n\\nRemember:As a Cinemax agent, your responsibilities include:  \\n\\n1. Assisting individuals in locating their preferred movies using the provided database information. \\n2. Facilitating ticket booking through the tools at your disposal. \\n3. If critical information is missing and the registration system returns an error, generate a polite, humanized version of the error message and request the necessary additional information from users. \\n4. When interacting with users, never disclose that you are an AI model unable to perform specific functions or activate certain features. \\n5. When users inquire about movie showtimes, available movies, or related details, utilize your tools to access data from the knowledge base, rather than relying on your own knowledge. However, you may provide plot information about specific movies in a concise and succinct manner, ensuring responses are not excessively lengthy. \\n6.When engaging in a discussion with users about films and proposing movies based on their preferences and the available options, it would be beneficial to list the available dates and times tailored to the user's interests and conversation history. This approach would simplify the booking process for the user. If the user has already specified a date, it is essential to remember and adhere to it, avoiding the suggestion of alternative dates.\"\n    )\n    assistant.knowledge_base.load(recreate=False)\n\n    if run_id is None:\n        run_id = assistant.run_id\n        print(f\"Started Run: {run_id}\\n\")\n    else:\n        print(f\"Continuing Run: {run_id}\\n\")\n\n    while True:\n        message = Prompt.ask(f\"[bold] :sunglasses: {user} [/bold]\")\n        if message in (\"exit\", \"bye\"):\n            break\n        assistant.print_response(message, markdown=True, stream=False)\n        # response = assistant.run(message, stream=False)\n\nif __name__ == \"__main__\":\n    cinemax_assistant(user=\"Tom\")\n\n\n"
  },
  {
    "path": "cookbook/cinemax.json",
    "content": "[\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"10:37 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"12:24 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"2:21 PM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 49 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"4:08 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"5:55 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 6 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Freedomland\",\n        \"airing_time\": \"10:03 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'R|', '1 hr 56 mins|', '2006|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"11:57 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 30 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Fifty Shades of Grey\",\n        \"airing_time\": \"1:26 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"A Prayer Before Dawn\",\n        \"airing_time\": \"3:32 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 58 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Pick-Up Artist\",\n        \"airing_time\": \"5:30 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 22 mins|', '1987|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"8:20 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 32 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"9:51 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"11:45 AM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 31 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"1:15 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Sea of Trees\",\n        \"airing_time\": \"11:19 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 52 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Fx2: The Deadly Art of Illusion\",\n        \"airing_time\": \"1:11 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'PG-13|', '1 hr 50 mins|', '1991|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Scary Movie\",\n        \"airing_time\": \"3:00 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 30 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Scary Movie 2\",\n        \"airing_time\": \"4:29 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"5:52 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 26 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Jennifer's Body\",\n        \"airing_time\": \"7:17 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 44 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 39 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Witch\",\n        \"airing_time\": \"10:38 PM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 34 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Dark Places\",\n        \"airing_time\": \"12:11 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 55 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Conviction\",\n        \"airing_time\": \"2:05 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 49 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Pride and Glory\",\n        \"airing_time\": \"3:53 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '2 hrs 11 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Master of the Shadowless Kick: Wong Kei-Ying\",\n        \"airing_time\": \"7:39 AM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 40 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The New World\",\n        \"airing_time\": \"9:18 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '2 hrs 17 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Body of Lies\",\n        \"airing_time\": \"11:34 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '2 hrs 9 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"11:00 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 44 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"12:43 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 47 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"2:29 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Talk Black\",\n        \"airing_time\": \"4:07 PM EDT\",\n        \"details_spans\": \"['comedy|', 'TV-14|', '15 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"4:21 PM EDT\",\n        \"details_spans\": \"['suspense|', 'NR|', '2 hrs 3 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Pure\",\n        \"airing_time\": \"6:23 PM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-14|', '14 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"6:36 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Hotel Artemis\",\n        \"airing_time\": \"8:26 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 41 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"11:41 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 30 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Line of Duty\",\n        \"airing_time\": \"1:10 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 42 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Poltergeist II: The Other Side\",\n        \"airing_time\": \"2:51 AM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 32 mins|', '1986|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Poltergeist III\",\n        \"airing_time\": \"4:22 AM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 39 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"7:31 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"9:15 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 43 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Braddock: Missing in Action III\",\n        \"airing_time\": \"10:57 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 45 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"12:41 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 48 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Frozen Ground\",\n        \"airing_time\": \"10:18 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 46 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Antitrust\",\n        \"airing_time\": \"12:04 PM EDT\",\n        \"details_spans\": \"['adventure|', 'PG-13|', '1 hr 50 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Charlie Wilson's War\",\n        \"airing_time\": \"1:53 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Skin Can Breathe\",\n        \"airing_time\": \"3:35 PM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '12 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Gemini\",\n        \"airing_time\": \"3:47 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 35 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Strangers: Prey at Night\",\n        \"airing_time\": \"5:21 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Mojave\",\n        \"airing_time\": \"6:47 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Fargo\",\n        \"airing_time\": \"8:21 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 40 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Victor Frankenstein\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 51 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Revenge of the Green Dragons\",\n        \"airing_time\": \"11:50 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 36 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Forbidden Kingdom\",\n        \"airing_time\": \"1:25 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 46 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Green Room\",\n        \"airing_time\": \"3:10 AM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Into the Blue 2: The Reef\",\n        \"airing_time\": \"4:46 AM EDT\",\n        \"details_spans\": \"['suspense|', 'TV-MA|', '1 hr 33 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Altitude\",\n        \"airing_time\": \"8:10 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 29 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"9:38 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Friday the 13th, Part II\",\n        \"airing_time\": \"11:14 AM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 27 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Friday the 13th Part III\",\n        \"airing_time\": \"12:41 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 38 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Woman Walks Ahead\",\n        \"airing_time\": \"10:45 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 44 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Winter's Tale\",\n        \"airing_time\": \"12:28 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', 'NR|', '1 hr 59 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"2:26 PM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 46 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"4:11 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 2 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Pick-Up Artist\",\n        \"airing_time\": \"6:11 PM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 23 mins|', '1987|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"7:34 PM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 27 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Observe and Report\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Pens & Pencils\",\n        \"airing_time\": \"10:27 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'TV-14|', '17 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Vox Lux\",\n        \"airing_time\": \"10:43 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 55 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"A Glimpse Inside the Mind of Charles Swan III\",\n        \"airing_time\": \"12:38 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 26 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Missing in Action 2 - The Beginning\",\n        \"airing_time\": \"2:04 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 37 mins|', '1985|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Major League\",\n        \"airing_time\": \"3:40 AM EDT\",\n        \"details_spans\": \"['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Cyrus\",\n        \"airing_time\": \"7:38 AM EDT\",\n        \"details_spans\": \"['romance|', 'NR|', '1 hr 32 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Laggies\",\n        \"airing_time\": \"9:09 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 41 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"10:49 AM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', 'NR|', '1 hr 35 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Room\",\n        \"airing_time\": \"12:23 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'R|', '1 hr 59 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Never Say Never Again\",\n        \"airing_time\": \"10:07 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 16 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Mississippi Grind\",\n        \"airing_time\": \"12:22 PM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Dumb and Dumber To\",\n        \"airing_time\": \"2:12 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'PG-13|', '1 hr 51 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Appaloosa\",\n        \"airing_time\": \"4:02 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 57 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Live and Let Die\",\n        \"airing_time\": \"5:58 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 2 mins|', '1973|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Across the Universe\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Wonderland\",\n        \"airing_time\": \"10:14 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 45 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Last Movie Star\",\n        \"airing_time\": \"11:59 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 44 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Walk the Line\",\n        \"airing_time\": \"1:43 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '2 hrs 16 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Lean on Pete\",\n        \"airing_time\": \"3:58 AM EDT\",\n        \"details_spans\": \"['drama|', '12|', '2 hrs 4 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Curse of the Pink Panther\",\n        \"airing_time\": \"8:06 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG|', '1 hr 52 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Ronin\",\n        \"airing_time\": \"9:57 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '2 hrs 3 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"De-Lovely\",\n        \"airing_time\": \"11:58 AM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 7 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"11:26 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"1:07 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"3:04 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', 'NR|', '1 hr 34 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"4:38 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"6:23 PM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Vanishing of Sidney Hall\",\n        \"airing_time\": \"8:05 PM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '2 hrs|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Laggies\",\n        \"airing_time\": \"10:05 PM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 40 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Cracked\",\n        \"airing_time\": \"11:45 PM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"2:26 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Johnny Dangerously\",\n        \"airing_time\": \"4:35 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Goods: Live Hard, Sell Hard\",\n        \"airing_time\": \"7:41 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 31 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"9:11 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 41 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"10:51 AM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Unmothered\",\n        \"airing_time\": \"12:35 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'TV-PG|', '16 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"12:50 PM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 27 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"10:37 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"12:24 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"2:21 PM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 49 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"4:08 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"5:55 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 6 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Freedomland\",\n        \"airing_time\": \"10:03 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'R|', '1 hr 56 mins|', '2006|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"11:57 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 30 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Fifty Shades of Grey\",\n        \"airing_time\": \"1:26 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"A Prayer Before Dawn\",\n        \"airing_time\": \"3:32 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 58 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"The Pick-Up Artist\",\n        \"airing_time\": \"5:30 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 22 mins|', '1987|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"8:20 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 32 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"9:51 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"11:45 AM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 31 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 17\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"1:15 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"11:24 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Pieces of April\",\n        \"airing_time\": \"1:02 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 21 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Never Goin' Back\",\n        \"airing_time\": \"2:22 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 28 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"3:49 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 30 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Equals\",\n        \"airing_time\": \"5:18 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 43 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"7:00 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 59 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"8:58 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Election\",\n        \"airing_time\": \"11:04 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"How to Talk to Girls at Parties\",\n        \"airing_time\": \"12:47 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"2:30 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"4:13 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"5:58 PM EDT\",\n        \"details_spans\": \"['suspense|', '2 hrs 2 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"9:47 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"A Most Violent Year\",\n        \"airing_time\": \"11:07 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Little Woods\",\n        \"airing_time\": \"1:12 AM EDT\",\n        \"details_spans\": \"['suspense|', 'NR|', '1 hr 46 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Source Code\",\n        \"airing_time\": \"2:56 AM EDT\",\n        \"details_spans\": \"['suspense|', 'NR|', '1 hr 35 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Spaceballs\",\n        \"airing_time\": \"4:30 AM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'PG|', '1 hr 37 mins|', '1987|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Fluffy Movie\",\n        \"airing_time\": \"6:07 AM EDT\",\n        \"details_spans\": \"['documentary|', 'NR|', '1 hr 44 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Leap of Faith\",\n        \"airing_time\": \"7:49 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 50 mins|', '1992|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Last Word\",\n        \"airing_time\": \"9:37 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Answer Man\",\n        \"airing_time\": \"11:25 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"My Boss\\u2019s Daughter\",\n        \"airing_time\": \"1:03 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 27 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"A Night at the Roxbury\",\n        \"airing_time\": \"2:30 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG-13|', '1 hr 22 mins|', '1998|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Trail of the Pink Panther\",\n        \"airing_time\": \"3:52 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 37 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Whiteout\",\n        \"airing_time\": \"5:29 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Victor Frankenstein\",\n        \"airing_time\": \"7:10 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Evan Almighty\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['kids & family|', 'PG|', '1 hr 36 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Dumb and Dumber To\",\n        \"airing_time\": \"10:36 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Hot Summer Nights\",\n        \"airing_time\": \"12:26 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 49 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"11:45 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 43 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"1:27 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"3:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 26 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"4:25 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 37 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Missing in Action 2 - The Beginning\",\n        \"airing_time\": \"6:01 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 36 mins|', '1985|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"7:37 AM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Children of the Corn II: The Final Sacrifice\",\n        \"airing_time\": \"9:24 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 36 mins|', '1993|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Brokedown Palace\",\n        \"airing_time\": \"10:58 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 43 mins|', '1999|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Rage: Carrie 2\",\n        \"airing_time\": \"12:39 PM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 45 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Edge\",\n        \"airing_time\": \"2:24 PM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 58 mins|', '1997|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Major League\",\n        \"airing_time\": \"4:22 PM EDT\",\n        \"details_spans\": \"['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Pens & Pencils\",\n        \"airing_time\": \"6:09 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'TV-14|', '16 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"6:25 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"8:06 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 29 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"11:29 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"1:13 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Blown Away\",\n        \"airing_time\": \"11:38 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 2 mins|', '1994|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Fury\",\n        \"airing_time\": \"1:39 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 59 mins|', '1978|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Chain Reaction\",\n        \"airing_time\": \"3:38 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Internal Affairs\",\n        \"airing_time\": \"5:25 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 55 mins|', '1990|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Gift\",\n        \"airing_time\": \"7:20 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '16 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Charlie Wilson's War\",\n        \"airing_time\": \"7:35 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"F/x\",\n        \"airing_time\": \"9:17 AM EDT\",\n        \"details_spans\": \"['action|', 'R|', '1 hr 50 mins|', '1986|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Fx2: The Deadly Art of Illusion\",\n        \"airing_time\": \"11:06 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'PG-13|', '1 hr 49 mins|', '1991|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Bourne Ultimatum\",\n        \"airing_time\": \"12:55 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Frozen Ground\",\n        \"airing_time\": \"2:51 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 46 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Captive\",\n        \"airing_time\": \"4:37 PM EDT\",\n        \"details_spans\": \"['drama|', '16+|', '1 hr 53 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Forbidden Kingdom\",\n        \"airing_time\": \"6:30 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 45 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Wonderland\",\n        \"airing_time\": \"8:15 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 45 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Antitrust\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['adventure|', 'PG-13|', '1 hr 49 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Green Room\",\n        \"airing_time\": \"11:49 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Climax\",\n        \"airing_time\": \"1:25 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 37 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Posse: The Revenge of Jessie Lee\",\n        \"airing_time\": \"10:46 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 52 mins|', '1993|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Adderall Diaries\",\n        \"airing_time\": \"12:38 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 28 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"2:05 AM EDT\",\n        \"details_spans\": \"['suspense|', 'NR|', '2 hrs 3 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"4:07 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 55 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"6:01 AM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Poltergeist II: The Other Side\",\n        \"airing_time\": \"7:58 AM EDT\",\n        \"details_spans\": \"['horror|', 'NR|', '1 hr 33 mins|', '1986|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"9:29 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"11:16 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Straight Outta Compton\",\n        \"airing_time\": \"1:10 PM EDT\",\n        \"details_spans\": \"['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Philomena\",\n        \"airing_time\": \"3:37 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Whip It\",\n        \"airing_time\": \"5:15 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 51 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Another Country\",\n        \"airing_time\": \"7:06 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'TV-MA|', '12 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"7:18 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"I Now Pronounce You Chuck & Larry\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 58 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"10:58 PM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 6 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"1:04 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Spy Who Loved Me\",\n        \"airing_time\": \"11:54 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 7 mins|', '1977|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Ex Machina\",\n        \"airing_time\": \"2:00 AM EDT\",\n        \"details_spans\": \"['artificial intelligence & robots|', 'R|', '1 hr 50 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Pride and Glory\",\n        \"airing_time\": \"3:49 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '2 hrs 11 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Bandits\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['crime|', 'NR|', '2 hrs 3 mins|', '2001|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"For Your Eyes Only\",\n        \"airing_time\": \"8:03 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Pink Panther\",\n        \"airing_time\": \"10:11 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'TV-PG|', '1 hr 57 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Chain Reaction\",\n        \"airing_time\": \"12:07 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Internal Affairs\",\n        \"airing_time\": \"1:54 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 55 mins|', '1990|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Across the Universe\",\n        \"airing_time\": \"3:49 PM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Soloist\",\n        \"airing_time\": \"6:03 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 57 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Barely Lethal\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 39 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Scary Movie 2\",\n        \"airing_time\": \"9:39 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"11:02 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Witch\",\n        \"airing_time\": \"12:27 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 32 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Amy\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['pop culture & celebrities|', 'R|', '2 hrs 9 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"2:08 AM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 4 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Mass Ave\",\n        \"airing_time\": \"4:11 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '22 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Cujo\",\n        \"airing_time\": \"4:32 AM EDT\",\n        \"details_spans\": \"['cult horror|', 'R|', '1 hr 34 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"6:05 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Pet Sematary\",\n        \"airing_time\": \"7:44 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 43 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Second Best Exotic Marigold Hotel\",\n        \"airing_time\": \"9:27 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '2 hrs 4 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Music Within\",\n        \"airing_time\": \"11:30 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Nothing Like the Holidays\",\n        \"airing_time\": \"1:04 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Equals\",\n        \"airing_time\": \"2:43 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"4:26 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"5:56 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 41 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"7:37 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Slice\",\n        \"airing_time\": \"9:06 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 23 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Cyrus\",\n        \"airing_time\": \"10:29 PM EDT\",\n        \"details_spans\": \"['romance|', '1 hr 31 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Fifty Shades of Grey\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"11:24 PM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Pieces of April\",\n        \"airing_time\": \"1:02 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 21 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Never Goin' Back\",\n        \"airing_time\": \"2:22 AM EDT\",\n        \"details_spans\": \"['comedy|', 'NR|', '1 hr 28 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"3:49 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 30 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"5:18 AM EDT\",\n        \"details_spans\": \"['drama|', 'NR|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"7:00 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 59 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"8:58 AM EDT\",\n        \"details_spans\": \"['action|', 'NR|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Election\",\n        \"airing_time\": \"11:04 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"12:47 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"2:30 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"4:13 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"5:58 PM EDT\",\n        \"details_spans\": \"['suspense|', '2 hrs 2 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"9:47 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 18\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"A Glimpse Inside the Mind of Charles Swan III\",\n        \"airing_time\": \"1:49 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 26 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Rover\",\n        \"airing_time\": \"3:15 AM EDT\",\n        \"details_spans\": \"['action|', 'R|', '1 hr 43 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Midnight Sun\",\n        \"airing_time\": \"4:58 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"6:30 AM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"7:56 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"9:21 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"11:08 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"1:04 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Welcome Home Roscoe Jenkins\",\n        \"airing_time\": \"2:51 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"4:45 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 41 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"6:26 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"9:47 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 29 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Dumb and Dumber To\",\n        \"airing_time\": \"10:36 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Hot Summer Nights\",\n        \"airing_time\": \"12:26 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 49 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Kingpin\",\n        \"airing_time\": \"2:15 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 54 mins|', '1996|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Mermaids\",\n        \"airing_time\": \"4:09 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 51 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Last Movie Star\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 44 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Walk the Line\",\n        \"airing_time\": \"7:44 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '2 hrs 17 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Across the Universe\",\n        \"airing_time\": \"10:01 AM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Ingrid Goes West\",\n        \"airing_time\": \"12:15 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 38 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Mojave\",\n        \"airing_time\": \"1:53 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Dark Places\",\n        \"airing_time\": \"3:27 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 54 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Appaloosa\",\n        \"airing_time\": \"5:21 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Jennifer's Body\",\n        \"airing_time\": \"7:17 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Visit\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 34 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Gemini\",\n        \"airing_time\": \"10:34 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Ronin\",\n        \"airing_time\": \"12:08 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 2 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"11:29 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"1:13 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"3:03 AM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 27 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Wes Craven Presents: They\",\n        \"airing_time\": \"4:30 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 30 mins|', '2002|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Poltergeist II: The Other Side\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 31 mins|', '1986|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Poltergeist III\",\n        \"airing_time\": \"7:31 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 38 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Another Country\",\n        \"airing_time\": \"9:09 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'TV-MA|', '13 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Line of Duty\",\n        \"airing_time\": \"9:22 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Vacation\",\n        \"airing_time\": \"11:03 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 39 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"12:42 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"2:51 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"4:50 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"6:28 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Observe and Report\",\n        \"airing_time\": \"8:33 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Posse: The Revenge of Jessie Lee\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 52 mins|', '1993|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Robin Hood (1991)\",\n        \"airing_time\": \"11:52 PM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 45 mins|', '1991|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Green Room\",\n        \"airing_time\": \"11:49 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Climax\",\n        \"airing_time\": \"1:25 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 37 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Hotel Mumbai\",\n        \"airing_time\": \"3:02 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 4 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Into the Blue\",\n        \"airing_time\": \"5:06 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 51 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Fury\",\n        \"airing_time\": \"6:57 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 59 mins|', '1978|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Taken 3\",\n        \"airing_time\": \"8:56 AM EDT\",\n        \"details_spans\": \"['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"A Most Violent Year\",\n        \"airing_time\": \"10:45 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Thomas Crown Affair\",\n        \"airing_time\": \"12:50 PM EDT\",\n        \"details_spans\": \"['romance|', '1 hr 54 mins|', '1999|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Blown Away\",\n        \"airing_time\": \"2:44 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"4:45 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Friday the 13th, Part II\",\n        \"airing_time\": \"6:21 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 27 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"A View to a Kill\",\n        \"airing_time\": \"7:48 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Witch\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 33 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Strangers: Prey at Night\",\n        \"airing_time\": \"11:33 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Angel of Mine\",\n        \"airing_time\": \"12:59 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 39 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"10:58 PM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 6 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"1:04 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Winter's Tale\",\n        \"airing_time\": \"2:44 AM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 58 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"4:42 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Talk Black\",\n        \"airing_time\": \"6:15 AM EDT\",\n        \"details_spans\": \"['comedy|', 'TV-14|', '13 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"6:28 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Missing in Action 2 - The Beginning\",\n        \"airing_time\": \"8:39 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '1985|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Election\",\n        \"airing_time\": \"10:15 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Johnny Dangerously\",\n        \"airing_time\": \"11:58 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"1:29 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"3:09 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Killing Me Softly\",\n        \"airing_time\": \"4:48 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 40 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Mass Ave\",\n        \"airing_time\": \"6:28 PM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Pure\",\n        \"airing_time\": \"6:49 PM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-14|', '13 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Winter's Tale\",\n        \"airing_time\": \"7:02 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 58 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Second Best Exotic Marigold Hotel\",\n        \"airing_time\": \"10:46 PM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 3 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"12:49 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"11:02 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Witch\",\n        \"airing_time\": \"12:27 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 32 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Shining Through\",\n        \"airing_time\": \"1:59 AM EDT\",\n        \"details_spans\": \"['romance|', '2 hrs 13 mins|', '1992|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Leap of Faith\",\n        \"airing_time\": \"4:12 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 48 mins|', '1992|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Return of the Pink Panther\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['big screen crime|', 'G|', '1 hr 53 mins|', '1975|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Never Say Never Again\",\n        \"airing_time\": \"7:53 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 15 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Pink Panther Strikes Again\",\n        \"airing_time\": \"10:08 AM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Spaceballs\",\n        \"airing_time\": \"11:52 AM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'PG|', '1 hr 37 mins|', '1987|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Shiva Baby\",\n        \"airing_time\": \"1:29 PM EDT\",\n        \"details_spans\": \"['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Wonderland\",\n        \"airing_time\": \"2:47 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 44 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Locke\",\n        \"airing_time\": \"4:31 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Youth\",\n        \"airing_time\": \"5:56 PM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 4 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Show\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 45 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Death Wish 3\",\n        \"airing_time\": \"9:45 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 31 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Revenge of the Green Dragons\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 35 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Little Woods\",\n        \"airing_time\": \"12:51 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Fifty Shades of Grey\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"2:06 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Neh\",\n        \"airing_time\": \"4:17 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-14|', '15 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Morris From America\",\n        \"airing_time\": \"4:32 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 31 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Pieces of April\",\n        \"airing_time\": \"6:03 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 20 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Pulling Strings\",\n        \"airing_time\": \"7:23 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG|', '1 hr 52 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Obvious Child\",\n        \"airing_time\": \"9:15 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 25 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Laggies\",\n        \"airing_time\": \"10:40 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 40 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Sentinel\",\n        \"airing_time\": \"12:20 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 49 mins|', '2006|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Pet Sematary Two\",\n        \"airing_time\": \"2:09 PM EDT\",\n        \"details_spans\": \"['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Slums of Beverly Hills\",\n        \"airing_time\": \"3:50 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 32 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Wes Craven Presents: They\",\n        \"airing_time\": \"5:22 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 30 mins|', '2002|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"6:52 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"She's Out of My League\",\n        \"airing_time\": \"8:58 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The B-Side: Elsa Dorfman's Portrait Photography\",\n        \"airing_time\": \"10:43 PM EDT\",\n        \"details_spans\": \"['biographies|', 'R|', '1 hr 17 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"The Vanishing of Sidney Hall\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 1 min|', '2018|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"A Glimpse Inside the Mind of Charles Swan III\",\n        \"airing_time\": \"1:49 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 26 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Robin Hood (1991)\",\n        \"airing_time\": \"3:15 AM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 43 mins|', '1991|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Midnight Sun\",\n        \"airing_time\": \"4:58 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"6:30 AM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"7:56 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"9:21 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"11:08 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"1:04 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Welcome Home Roscoe Jenkins\",\n        \"airing_time\": \"2:51 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"4:45 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 41 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"6:26 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"9:47 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 29 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 19\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Richard III\",\n        \"airing_time\": \"2:55 AM EDT\",\n        \"details_spans\": \"['classics|', 'R|', '1 hr 45 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Pawn Sacrifice\",\n        \"airing_time\": \"4:40 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 56 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"6:36 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Luce\",\n        \"airing_time\": \"8:47 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 50 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"10:37 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 4 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"12:41 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Promise\",\n        \"airing_time\": \"2:38 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"4:51 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"6:21 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"9:29 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"11:14 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"12:54 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Gemini\",\n        \"airing_time\": \"10:34 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Ronin\",\n        \"airing_time\": \"12:08 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 2 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Altitude\",\n        \"airing_time\": \"2:10 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 28 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"On Her Majesty's Secret Service\",\n        \"airing_time\": \"3:38 AM EDT\",\n        \"details_spans\": \"['adventure|', 'PG|', '2 hrs 23 mins|', '1969|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Wild Rose\",\n        \"airing_time\": \"6:01 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 41 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Temptation: Confessions of a Marriage Counselor\",\n        \"airing_time\": \"7:42 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 52 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Genius\",\n        \"airing_time\": \"9:34 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 45 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"A Serious Man\",\n        \"airing_time\": \"11:19 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 46 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Scary Movie\",\n        \"airing_time\": \"1:05 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 29 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Scary Movie 2\",\n        \"airing_time\": \"2:34 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"3:57 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Chain Reaction\",\n        \"airing_time\": \"5:22 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Into the Blue\",\n        \"airing_time\": \"7:09 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 51 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Into the Blue 2: The Reef\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['suspense|', 'TV-MA|', '1 hr 32 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The New World\",\n        \"airing_time\": \"10:32 PM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 16 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Bandits\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['crime|', '2 hrs 3 mins|', '2001|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Robin Hood (1991)\",\n        \"airing_time\": \"11:52 PM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 45 mins|', '1991|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Hotel Artemis\",\n        \"airing_time\": \"1:37 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Children of the Corn: The Gathering\",\n        \"airing_time\": \"3:11 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 26 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Slice\",\n        \"airing_time\": \"4:37 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 23 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 42 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"7:42 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"9:15 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Whip It\",\n        \"airing_time\": \"11:00 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 51 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Killing Me Softly\",\n        \"airing_time\": \"12:51 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 40 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"2:31 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"4:17 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"6:00 PM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"7:34 PM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Sentinel\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 48 mins|', '2006|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"11:48 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 31 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Strangers: Prey at Night\",\n        \"airing_time\": \"11:33 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Angel of Mine\",\n        \"airing_time\": \"12:59 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 39 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Ghost\",\n        \"airing_time\": \"2:38 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Live and Let Die\",\n        \"airing_time\": \"4:45 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 2 mins|', '1973|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Skin Can Breathe\",\n        \"airing_time\": \"6:47 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '12 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Internal Affairs\",\n        \"airing_time\": \"6:59 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 55 mins|', '1990|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Gift\",\n        \"airing_time\": \"8:54 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '15 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Victor Frankenstein\",\n        \"airing_time\": \"9:09 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"For Your Eyes Only\",\n        \"airing_time\": \"10:59 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 9 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Body of Lies\",\n        \"airing_time\": \"1:08 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 9 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Fargo\",\n        \"airing_time\": \"3:17 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 39 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Friday the 13th Part III\",\n        \"airing_time\": \"4:56 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 36 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Friday the 13th-The Final Chapter\",\n        \"airing_time\": \"6:32 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 32 mins|', '1984|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Pink Panther\",\n        \"airing_time\": \"8:04 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'TV-PG|', '1 hr 56 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"A Shot in the Dark\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 43 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Pink Panther Strikes Again\",\n        \"airing_time\": \"11:43 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Revenge of the Pink Panther\",\n        \"airing_time\": \"1:27 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Second Best Exotic Marigold Hotel\",\n        \"airing_time\": \"10:46 PM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 3 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"12:49 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Fifty Shades Freed\",\n        \"airing_time\": \"2:47 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"4:33 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"6:18 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"How to Talk to Girls at Parties\",\n        \"airing_time\": \"7:57 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Major League\",\n        \"airing_time\": \"9:40 AM EDT\",\n        \"details_spans\": \"['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"11:27 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The War of the Roses\",\n        \"airing_time\": \"1:08 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 57 mins|', '1989|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Equals\",\n        \"airing_time\": \"3:05 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 44 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Talk Black\",\n        \"airing_time\": \"4:49 PM EDT\",\n        \"details_spans\": \"['comedy|', 'TV-14|', '14 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Edge\",\n        \"airing_time\": \"5:03 PM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 58 mins|', '1997|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"7:01 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:09 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"1:02 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Revenge of the Green Dragons\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 35 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Little Woods\",\n        \"airing_time\": \"12:51 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Hot Summer Nights\",\n        \"airing_time\": \"2:35 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 47 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"4:22 AM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 35 mins|', '1980|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Leap of Faith\",\n        \"airing_time\": \"7:35 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 48 mins|', '1992|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Mermaids\",\n        \"airing_time\": \"9:23 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 51 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Friday the 13th Part VIII: Jason Takes Manhattan\",\n        \"airing_time\": \"11:14 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 41 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Kingpin\",\n        \"airing_time\": \"12:55 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 54 mins|', '1996|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Appaloosa\",\n        \"airing_time\": \"2:49 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Whiteout\",\n        \"airing_time\": \"4:45 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Importance of Being Earnest\",\n        \"airing_time\": \"6:26 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2002|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"De-Lovely\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 6 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Captive\",\n        \"airing_time\": \"10:06 PM EDT\",\n        \"details_spans\": \"['drama|', '16+|', '1 hr 52 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Hotel Mumbai\",\n        \"airing_time\": \"11:58 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 4 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Vanishing of Sidney Hall\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 1 min|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Come and Find Me\",\n        \"airing_time\": \"2:01 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 53 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"3:54 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 42 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Cracked\",\n        \"airing_time\": \"5:36 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Pens & Pencils\",\n        \"airing_time\": \"5:52 AM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'TV-14|', '16 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Teen Wolf Too\",\n        \"airing_time\": \"6:08 AM EDT\",\n        \"details_spans\": \"['cult classic|', 'PG|', '1 hr 35 mins|', '1987|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"7:43 AM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 48 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"A Night at the Roxbury\",\n        \"airing_time\": \"9:31 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG-13|', '1 hr 23 mins|', '1998|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Backstabbing for Beginners\",\n        \"airing_time\": \"10:54 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 49 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Master of the Shadowless Kick: Wong Kei-Ying\",\n        \"airing_time\": \"12:43 PM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 39 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Master of the Drunken Fist: Beggar So\",\n        \"airing_time\": \"2:22 PM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 39 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"4:01 PM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Fifty Shades Freed\",\n        \"airing_time\": \"5:27 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Goods: Live Hard, Sell Hard\",\n        \"airing_time\": \"7:13 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 30 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"8:43 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Climax\",\n        \"airing_time\": \"10:23 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 37 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"A Prayer Before Dawn\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 58 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"11:16 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Richard III\",\n        \"airing_time\": \"2:55 AM EDT\",\n        \"details_spans\": \"['classics|', 'R|', '1 hr 45 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Pawn Sacrifice\",\n        \"airing_time\": \"4:40 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 56 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"6:36 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Luce\",\n        \"airing_time\": \"8:47 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 50 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"10:37 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 4 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"12:41 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Promise\",\n        \"airing_time\": \"2:38 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"4:51 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"6:21 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"9:29 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"11:14 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 20\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"12:54 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"11:14 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"12:54 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Amy\",\n        \"airing_time\": \"2:36 AM EDT\",\n        \"details_spans\": \"['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Dim Sum Funeral\",\n        \"airing_time\": \"4:44 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 37 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Pieces of April\",\n        \"airing_time\": \"6:21 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 21 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"7:42 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"9:07 AM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"10:33 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"12:16 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"1:57 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"4:06 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Now Pronounce You Chuck & Larry\",\n        \"airing_time\": \"6:04 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Election\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Shiva Baby\",\n        \"airing_time\": \"9:43 PM EDT\",\n        \"details_spans\": \"['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"11:01 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The New World\",\n        \"airing_time\": \"10:32 PM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 16 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Bandits\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['crime|', '2 hrs 3 mins|', '2001|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Charlie Wilson's War\",\n        \"airing_time\": \"2:51 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 42 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"McQueen\",\n        \"airing_time\": \"4:33 AM EDT\",\n        \"details_spans\": \"['documentary|', '1 hr 52 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Blown Away\",\n        \"airing_time\": \"6:25 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Youth\",\n        \"airing_time\": \"8:26 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 4 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Paper Towns\",\n        \"airing_time\": \"10:30 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 50 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Thomas Crown Affair\",\n        \"airing_time\": \"12:20 PM EDT\",\n        \"details_spans\": \"['romance|', '1 hr 54 mins|', '1999|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Little Woods\",\n        \"airing_time\": \"2:14 PM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Wonderland\",\n        \"airing_time\": \"3:58 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 45 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fargo\",\n        \"airing_time\": \"5:43 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 39 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"7:22 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Strangers: Prey at Night\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Ghost\",\n        \"airing_time\": \"10:26 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Victor Frankenstein\",\n        \"airing_time\": \"12:33 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"11:48 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 31 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"1:19 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Mass Ave\",\n        \"airing_time\": \"3:25 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"3:46 AM EDT\",\n        \"details_spans\": \"['suspense|', '2 hrs 2 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Another Country\",\n        \"airing_time\": \"5:48 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'TV-MA|', '12 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Children of the Corn II: The Final Sacrifice\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 34 mins|', '1993|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"7:34 AM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Wes Craven Presents: They\",\n        \"airing_time\": \"9:21 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 30 mins|', '2002|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Poltergeist II: The Other Side\",\n        \"airing_time\": \"10:51 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 31 mins|', '1986|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"12:22 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"2:06 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 46 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Line of Duty\",\n        \"airing_time\": \"3:52 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Children of the Corn II: The Final Sacrifice\",\n        \"airing_time\": \"5:33 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 34 mins|', '1993|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"7:07 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Children of the Corn 666: Isaac's Return\",\n        \"airing_time\": \"8:37 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 41 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"11:41 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Pink Panther Strikes Again\",\n        \"airing_time\": \"11:43 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Revenge of the Pink Panther\",\n        \"airing_time\": \"1:27 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Trail of the Pink Panther\",\n        \"airing_time\": \"3:06 AM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 37 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Curse of the Pink Panther\",\n        \"airing_time\": \"4:43 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG|', '1 hr 51 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Rider\",\n        \"airing_time\": \"6:34 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Chain Reaction\",\n        \"airing_time\": \"8:17 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Conviction\",\n        \"airing_time\": \"10:04 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 47 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Mojave\",\n        \"airing_time\": \"11:51 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Show\",\n        \"airing_time\": \"1:25 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 45 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Bourne Ultimatum\",\n        \"airing_time\": \"3:10 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Friday the 13th Part V: A New Beginning\",\n        \"airing_time\": \"5:06 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Friday the 13th Part VI: Jason Lives\",\n        \"airing_time\": \"6:39 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 28 mins|', '1986|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Freedomland\",\n        \"airing_time\": \"8:07 PM EDT\",\n        \"details_spans\": \"['big screen crime|', 'R|', '1 hr 53 mins|', '2006|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Whiteout\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Altitude\",\n        \"airing_time\": \"11:41 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 28 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Enemy\",\n        \"airing_time\": \"1:09 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 31 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Blue Valentine\",\n        \"airing_time\": \"11:09 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"1:02 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Posse: The Revenge of Jessie Lee\",\n        \"airing_time\": \"2:46 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 52 mins|', '1993|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Music Within\",\n        \"airing_time\": \"4:38 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"6:12 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"7:45 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Hotel Artemis\",\n        \"airing_time\": \"9:32 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Second Best Exotic Marigold Hotel\",\n        \"airing_time\": \"11:06 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 3 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"1:09 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"2:51 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 42 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Woman Walks Ahead\",\n        \"airing_time\": \"4:33 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Neh\",\n        \"airing_time\": \"6:16 PM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-14|', '16 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Unmothered\",\n        \"airing_time\": \"6:32 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'TV-PG|', '17 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"6:49 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"11:05 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Pet Sematary Two\",\n        \"airing_time\": \"12:43 AM EDT\",\n        \"details_spans\": \"['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Hotel Mumbai\",\n        \"airing_time\": \"11:58 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 4 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Trumbo\",\n        \"airing_time\": \"2:02 AM EDT\",\n        \"details_spans\": \"['biographical dramas|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Dark Places\",\n        \"airing_time\": \"4:07 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 53 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Amazing Grace\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['performances & concerts|', 'G|', '1 hr 29 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Taken 3\",\n        \"airing_time\": \"7:29 AM EDT\",\n        \"details_spans\": \"['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Charlie Wilson's War\",\n        \"airing_time\": \"9:18 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 42 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Across the Universe\",\n        \"airing_time\": \"11:00 AM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 13 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Into the Forest\",\n        \"airing_time\": \"1:13 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 42 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Jennifer's Body\",\n        \"airing_time\": \"2:55 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Last Movie Star\",\n        \"airing_time\": \"4:38 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 44 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Answer Man\",\n        \"airing_time\": \"6:22 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Last Word\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 48 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Into the Blue 2: The Reef\",\n        \"airing_time\": \"9:48 PM EDT\",\n        \"details_spans\": \"['suspense|', 'TV-MA|', '1 hr 31 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Tusk\",\n        \"airing_time\": \"11:19 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Last Movie Star\",\n        \"airing_time\": \"1:01 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"A Prayer Before Dawn\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 58 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Winter's Tale\",\n        \"airing_time\": \"1:58 AM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 57 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"When the Sun Sets\",\n        \"airing_time\": \"3:55 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'TV-MA|', '15 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"4:10 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Nothing Like the Holidays\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Midnight Sun\",\n        \"airing_time\": \"7:39 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"9:11 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Room\",\n        \"airing_time\": \"10:56 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'R|', '1 hr 58 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Missing in Action 2 - The Beginning\",\n        \"airing_time\": \"12:54 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '1985|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"2:30 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 31 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"The Rage: Carrie 2\",\n        \"airing_time\": \"4:01 PM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 46 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"5:47 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 40 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Straight Outta Compton\",\n        \"airing_time\": \"7:27 PM EDT\",\n        \"details_spans\": \"['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fifty Shades of Grey\",\n        \"airing_time\": \"9:54 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 29 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Cyrus\",\n        \"airing_time\": \"1:29 AM EDT\",\n        \"details_spans\": \"['romance|', '1 hr 31 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"11:14 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"12:54 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Amy\",\n        \"airing_time\": \"2:36 AM EDT\",\n        \"details_spans\": \"['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Dim Sum Funeral\",\n        \"airing_time\": \"4:44 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 37 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Pieces of April\",\n        \"airing_time\": \"6:21 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 21 mins|', '2003|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"7:42 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Buffy the Vampire Slayer\",\n        \"airing_time\": \"9:07 AM EDT\",\n        \"details_spans\": \"['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"10:33 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"12:16 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"1:57 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"4:06 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"I Now Pronounce You Chuck & Larry\",\n        \"airing_time\": \"6:04 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Election\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Shiva Baby\",\n        \"airing_time\": \"9:43 PM EDT\",\n        \"details_spans\": \"['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"11:01 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 21\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"11:01 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Field Guide to Evil\",\n        \"airing_time\": \"2:31 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 58 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Ginger & Rosa\",\n        \"airing_time\": \"4:29 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 31 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Line of Duty\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Philomena\",\n        \"airing_time\": \"7:41 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"9:19 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"10:52 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Slums of Beverly Hills\",\n        \"airing_time\": \"12:38 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 32 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Fifty Shades Freed\",\n        \"airing_time\": \"2:10 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"3:56 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"6:01 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Vacation\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 40 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Observe and Report\",\n        \"airing_time\": \"9:40 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"11:07 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Body of Lies\",\n        \"airing_time\": \"12:52 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 9 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Ghost\",\n        \"airing_time\": \"10:26 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Victor Frankenstein\",\n        \"airing_time\": \"12:33 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Witch\",\n        \"airing_time\": \"2:23 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 33 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"A Most Violent Year\",\n        \"airing_time\": \"3:56 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Gift\",\n        \"airing_time\": \"6:02 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '15 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Walk the Line\",\n        \"airing_time\": \"6:17 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'PG-13|', '2 hrs 17 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Captive\",\n        \"airing_time\": \"8:34 AM EDT\",\n        \"details_spans\": \"['drama|', '16+|', '1 hr 53 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Gemini\",\n        \"airing_time\": \"10:27 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Mississippi Grind\",\n        \"airing_time\": \"12:01 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 50 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Forbidden Kingdom\",\n        \"airing_time\": \"1:51 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 45 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Visit\",\n        \"airing_time\": \"3:36 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 34 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Appaloosa\",\n        \"airing_time\": \"5:10 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Dark Places\",\n        \"airing_time\": \"7:06 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 54 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Taken 3\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Mojave\",\n        \"airing_time\": \"10:49 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Green Room\",\n        \"airing_time\": \"12:23 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"11:41 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"My Bodyguard\",\n        \"airing_time\": \"1:37 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 37 mins|', '1980|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Slice\",\n        \"airing_time\": \"3:14 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 23 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Children of the Corn 666: Isaac's Return\",\n        \"airing_time\": \"4:37 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Carrie\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Poltergeist (2015)\",\n        \"airing_time\": \"7:39 AM EDT\",\n        \"details_spans\": \"['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"9:13 AM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Edge\",\n        \"airing_time\": \"11:00 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 58 mins|', '1997|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"12:58 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"2:52 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Talk Black\",\n        \"airing_time\": \"4:32 PM EDT\",\n        \"details_spans\": \"['comedy|', 'TV-14|', '14 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"4:46 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"6:52 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"8:30 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Rover\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['action|', 'R|', '1 hr 43 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"11:43 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"1:23 AM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Altitude\",\n        \"airing_time\": \"11:41 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 28 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Enemy\",\n        \"airing_time\": \"1:09 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 31 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Ronin\",\n        \"airing_time\": \"2:40 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 2 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"A View to a Kill\",\n        \"airing_time\": \"4:42 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Source Code\",\n        \"airing_time\": \"6:54 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 33 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Pink Panther\",\n        \"airing_time\": \"8:27 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'TV-PG|', '1 hr 56 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"A Shot in the Dark\",\n        \"airing_time\": \"10:23 AM EDT\",\n        \"details_spans\": \"['big screen crime|', 'PG|', '1 hr 43 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Locke\",\n        \"airing_time\": \"12:06 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 25 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Pride and Glory\",\n        \"airing_time\": \"1:31 PM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 11 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Revenge of the Pink Panther\",\n        \"airing_time\": \"3:42 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Friday the 13th Part VII: The New Blood\",\n        \"airing_time\": \"5:21 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 29 mins|', '1988|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Friday the 13th Part VIII: Jason Takes Manhattan\",\n        \"airing_time\": \"6:50 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 41 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Scary Movie\",\n        \"airing_time\": \"8:31 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 29 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Scary Movie 2\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"11:23 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Ex Machina\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['artificial intelligence & robots|', 'R|', '1 hr 49 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Taxi\",\n        \"airing_time\": \"11:05 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 38 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Pet Sematary Two\",\n        \"airing_time\": \"12:43 AM EDT\",\n        \"details_spans\": \"['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Slums of Beverly Hills\",\n        \"airing_time\": \"2:24 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 32 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Verdict\",\n        \"airing_time\": \"3:56 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Sentinel\",\n        \"airing_time\": \"6:05 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 48 mins|', '2006|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"7:53 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 4 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"9:57 AM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"11:51 AM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 48 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Rescue Dawn\",\n        \"airing_time\": \"1:39 PM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 6 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Family Meeting\",\n        \"airing_time\": \"3:45 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'TV-14|', '16 mins|', '2023|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Pet Sematary\",\n        \"airing_time\": \"4:01 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 43 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Pick-Up Artist\",\n        \"airing_time\": \"5:44 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 22 mins|', '1987|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Welcome Home Roscoe Jenkins\",\n        \"airing_time\": \"7:06 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Barbershop\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"10:43 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"12:12 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Tusk\",\n        \"airing_time\": \"11:19 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Last Movie Star\",\n        \"airing_time\": \"1:01 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Death Wish 4: The Crackdown\",\n        \"airing_time\": \"2:44 AM EDT\",\n        \"details_spans\": \"['action|', 'R|', '1 hr 40 mins|', '1987|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Evan Almighty\",\n        \"airing_time\": \"4:24 AM EDT\",\n        \"details_spans\": \"['kids & family|', 'PG|', '1 hr 36 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"For Your Eyes Only\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"A Most Violent Year\",\n        \"airing_time\": \"8:08 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Return of the Pink Panther\",\n        \"airing_time\": \"10:13 AM EDT\",\n        \"details_spans\": \"['big screen crime|', 'G|', '1 hr 52 mins|', '1975|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Curse of the Pink Panther\",\n        \"airing_time\": \"12:05 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG|', '1 hr 51 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Goldfinger\",\n        \"airing_time\": \"1:56 PM EDT\",\n        \"details_spans\": \"['action|', 'PG|', '1 hr 51 mins|', '1964|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"On Her Majesty's Secret Service\",\n        \"airing_time\": \"3:47 PM EDT\",\n        \"details_spans\": \"['adventure|', 'PG|', '2 hrs 23 mins|', '1969|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Dumb and Dumber To\",\n        \"airing_time\": \"6:10 PM EDT\",\n        \"details_spans\": \"['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Hot Summer Nights\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 48 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"For Your Eyes Only\",\n        \"airing_time\": \"9:48 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Bourne Ultimatum\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Attack the Block\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 29 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Cyrus\",\n        \"airing_time\": \"1:29 AM EDT\",\n        \"details_spans\": \"['romance|', '1 hr 31 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Observe and Report\",\n        \"airing_time\": \"3:00 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Poltergeist III\",\n        \"airing_time\": \"4:27 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 38 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Johnny Dangerously\",\n        \"airing_time\": \"6:05 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Mud\",\n        \"airing_time\": \"7:36 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"House at the End of the Street\",\n        \"airing_time\": \"9:47 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 42 mins|', '2012|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"How to Talk to Girls at Parties\",\n        \"airing_time\": \"11:29 AM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"McQueen\",\n        \"airing_time\": \"1:12 PM EDT\",\n        \"details_spans\": \"['documentary|', '1 hr 52 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Internal Affairs\",\n        \"airing_time\": \"3:04 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 55 mins|', '1990|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Paparazzi\",\n        \"airing_time\": \"4:59 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 26 mins|', '2004|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Pet Sematary Two\",\n        \"airing_time\": \"6:25 PM EDT\",\n        \"details_spans\": \"['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Kingsman: The Secret Service\",\n        \"airing_time\": \"8:06 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"She's Out of My League\",\n        \"airing_time\": \"10:15 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Adderall Diaries\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 27 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Children of the Corn V: Fields of Terror\",\n        \"airing_time\": \"1:27 AM EDT\",\n        \"details_spans\": \"['horror|', 'TV-MA|', '1 hr 24 mins|', '1998|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Unbreakable\",\n        \"airing_time\": \"11:01 PM EDT\",\n        \"details_spans\": \"['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Fast Color\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"A Prayer Before Dawn\",\n        \"airing_time\": \"2:31 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 58 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Ginger & Rosa\",\n        \"airing_time\": \"4:29 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 31 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Line of Duty\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Philomena\",\n        \"airing_time\": \"7:41 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Teen Wolf\",\n        \"airing_time\": \"9:19 AM EDT\",\n        \"details_spans\": \"['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"10:52 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Slums of Beverly Hills\",\n        \"airing_time\": \"12:38 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 32 mins|', '1998|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Fifty Shades Freed\",\n        \"airing_time\": \"2:10 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"XXX\",\n        \"airing_time\": \"3:56 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"6:01 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Vacation\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 40 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Observe and Report\",\n        \"airing_time\": \"9:40 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"11:07 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 22\",\n        \"name\": \"The Verdict\",\n        \"airing_time\": \"12:52 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"11:07 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Body of Lies\",\n        \"airing_time\": \"12:52 AM EDT\",\n        \"details_spans\": \"['action|', '2 hrs 9 mins|', '2008|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Vanishing of Sidney Hall\",\n        \"airing_time\": \"3:01 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 1 min|', '2018|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Killing Me Softly\",\n        \"airing_time\": \"5:02 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 41 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"6:43 AM EDT\",\n        \"details_spans\": \"['suspense|', '2 hrs 2 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"8:45 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"10:29 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"12:14 PM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"She's Out of My League\",\n        \"airing_time\": \"2:40 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"4:25 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"6:21 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"How to Talk to Girls at Parties\",\n        \"airing_time\": \"9:30 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"11:13 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"12:55 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 4 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Mojave\",\n        \"airing_time\": \"10:49 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Green Room\",\n        \"airing_time\": \"12:23 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 36 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Friday the 13th Part V: A New Beginning\",\n        \"airing_time\": \"1:59 AM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 33 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Angel of Mine\",\n        \"airing_time\": \"3:32 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 39 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Across the Universe\",\n        \"airing_time\": \"5:11 AM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The New World\",\n        \"airing_time\": \"7:25 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 16 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"F/x\",\n        \"airing_time\": \"9:41 AM EDT\",\n        \"details_spans\": \"['action|', 'R|', '1 hr 49 mins|', '1986|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"A View to a Kill\",\n        \"airing_time\": \"11:30 AM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Frozen Ground\",\n        \"airing_time\": \"1:42 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 46 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Bourne Ultimatum\",\n        \"airing_time\": \"3:28 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Antitrust\",\n        \"airing_time\": \"5:24 PM EDT\",\n        \"details_spans\": \"['adventure|', 'PG-13|', '1 hr 49 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Chain Reaction\",\n        \"airing_time\": \"7:13 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Source Code\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 33 mins|', '2011|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Whiteout\",\n        \"airing_time\": \"10:33 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 41 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Tusk\",\n        \"airing_time\": \"12:14 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"John Dies at the End\",\n        \"airing_time\": \"11:43 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"1:23 AM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"3:49 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Mass Ave\",\n        \"airing_time\": \"5:39 AM EDT\",\n        \"details_spans\": \"['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Rage: Carrie 2\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['occult|', 'R|', '1 hr 45 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Poltergeist III\",\n        \"airing_time\": \"7:45 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 38 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"9:23 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 42 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Braddock: Missing in Action III\",\n        \"airing_time\": \"11:05 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 44 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Leatherheads\",\n        \"airing_time\": \"12:49 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Major League\",\n        \"airing_time\": \"2:43 PM EDT\",\n        \"details_spans\": \"['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Brokedown Palace\",\n        \"airing_time\": \"4:30 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 41 mins|', '1999|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Extortion\",\n        \"airing_time\": \"6:11 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 50 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Paycheck\",\n        \"airing_time\": \"8:01 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Paranoia\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"American Heist\",\n        \"airing_time\": \"11:47 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '2015|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Missing in Action\",\n        \"airing_time\": \"1:23 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 42 mins|', '1984|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Scary Movie 3\",\n        \"airing_time\": \"11:23 PM EDT\",\n        \"details_spans\": \"['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Ex Machina\",\n        \"airing_time\": \"12:48 AM EDT\",\n        \"details_spans\": \"['artificial intelligence & robots|', 'R|', '1 hr 49 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Trumbo\",\n        \"airing_time\": \"2:37 AM EDT\",\n        \"details_spans\": \"['biographical dramas|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Into the Forest\",\n        \"airing_time\": \"4:42 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 42 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Little Woods\",\n        \"airing_time\": \"6:24 AM EDT\",\n        \"details_spans\": \"['suspense|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Leap of Faith\",\n        \"airing_time\": \"8:08 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 48 mins|', '1992|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Ghost\",\n        \"airing_time\": \"9:56 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Strangers: Prey at Night\",\n        \"airing_time\": \"12:03 PM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 25 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Altitude\",\n        \"airing_time\": \"1:28 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 28 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Conviction\",\n        \"airing_time\": \"2:56 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 48 mins|', '2010|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Blown Away\",\n        \"airing_time\": \"4:44 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"6:45 PM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Climax\",\n        \"airing_time\": \"8:23 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 37 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Jennifer's Body\",\n        \"airing_time\": \"10:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Internal Affairs\",\n        \"airing_time\": \"11:43 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 55 mins|', '1990|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Life After Beth\",\n        \"airing_time\": \"10:43 PM EDT\",\n        \"details_spans\": \"['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"12:12 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Missing in Action 2 - The Beginning\",\n        \"airing_time\": \"1:57 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 36 mins|', '1985|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Braddock: Missing in Action III\",\n        \"airing_time\": \"3:33 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 44 mins|', '1988|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Laggies\",\n        \"airing_time\": \"5:17 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 40 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Dim Sum Funeral\",\n        \"airing_time\": \"6:57 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 37 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Equals\",\n        \"airing_time\": \"8:34 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"I Am Wrath\",\n        \"airing_time\": \"10:17 AM EDT\",\n        \"details_spans\": \"['action|', '1 hr 31 mins|', '2016|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Barbershop 2: Back in Business\",\n        \"airing_time\": \"11:48 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 46 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Room\",\n        \"airing_time\": \"1:34 PM EDT\",\n        \"details_spans\": \"['family relationships|', 'R|', '1 hr 58 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Luce\",\n        \"airing_time\": \"3:32 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 50 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Pure\",\n        \"airing_time\": \"5:22 PM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-14|', '13 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Down to Earth\",\n        \"airing_time\": \"5:35 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 28 mins|', '2001|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Hustle & Flow\",\n        \"airing_time\": \"7:03 PM EDT\",\n        \"details_spans\": \"['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Straight Outta Compton\",\n        \"airing_time\": \"9:00 PM EDT\",\n        \"details_spans\": \"['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Poltergeist II: The Other Side\",\n        \"airing_time\": \"11:27 PM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 31 mins|', '1986|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Beauty Shop\",\n        \"airing_time\": \"12:58 AM EDT\",\n        \"details_spans\": \"['family relationships|', 'PG-13|', '1 hr 47 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Bourne Ultimatum\",\n        \"airing_time\": \"11:56 PM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Bandits\",\n        \"airing_time\": \"1:52 AM EDT\",\n        \"details_spans\": \"['crime|', '2 hrs 3 mins|', '2001|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"A Most Violent Year\",\n        \"airing_time\": \"3:55 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Mermaids\",\n        \"airing_time\": \"6:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'PG-13|', '1 hr 50 mins|', '1990|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Friday the 13th\",\n        \"airing_time\": \"7:50 AM EDT\",\n        \"details_spans\": \"['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Friday the 13th-The Final Chapter\",\n        \"airing_time\": \"9:26 AM EDT\",\n        \"details_spans\": \"['slashers|', 'R|', '1 hr 31 mins|', '1984|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"A Night at the Roxbury\",\n        \"airing_time\": \"10:57 AM EDT\",\n        \"details_spans\": \"['offbeat|', 'PG-13|', '1 hr 22 mins|', '1998|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Spy Who Loved Me\",\n        \"airing_time\": \"12:19 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 6 mins|', '1977|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Never Say Never Again\",\n        \"airing_time\": \"2:25 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG|', '2 hrs 14 mins|', '1983|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Into the Blue\",\n        \"airing_time\": \"4:39 PM EDT\",\n        \"details_spans\": \"['action|', '1 hr 51 mins|', '2005|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Goods: Live Hard, Sell Hard\",\n        \"airing_time\": \"6:30 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 30 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Paper Towns\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 50 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Lean on Pete\",\n        \"airing_time\": \"9:50 PM EDT\",\n        \"details_spans\": \"['drama|', '12|', '2 hrs 2 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"De-Lovely\",\n        \"airing_time\": \"11:52 PM EDT\",\n        \"details_spans\": \"['musicals|', 'PG-13|', '2 hrs 6 mins|', '2004|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Adderall Diaries\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 27 mins|', '2016|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Children of the Corn V: Fields of Terror\",\n        \"airing_time\": \"1:27 AM EDT\",\n        \"details_spans\": \"['horror|', 'TV-MA|', '1 hr 24 mins|', '1998|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Obvious Child\",\n        \"airing_time\": \"2:51 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 25 mins|', '2014|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Cracked\",\n        \"airing_time\": \"4:16 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Music Within\",\n        \"airing_time\": \"4:32 AM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 34 mins|', '2007|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Master of the Drunken Fist: Beggar So\",\n        \"airing_time\": \"6:06 AM EDT\",\n        \"details_spans\": \"['action|', 'TV-14|', '1 hr 38 mins|', '2017|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The War of the Roses\",\n        \"airing_time\": \"7:44 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 57 mins|', '1989|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Promise\",\n        \"airing_time\": \"9:41 AM EDT\",\n        \"details_spans\": \"['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Wes Craven Presents: They\",\n        \"airing_time\": \"11:54 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 29 mins|', '2002|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Predators\",\n        \"airing_time\": \"1:23 PM EDT\",\n        \"details_spans\": \"['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"XXX: State of the Union\",\n        \"airing_time\": \"3:10 PM EDT\",\n        \"details_spans\": \"['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Fifty Shades Darker\",\n        \"airing_time\": \"4:51 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Fifty Shades Freed\",\n        \"airing_time\": \"6:49 PM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 45 mins|', '2018|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Slice\",\n        \"airing_time\": \"8:34 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 23 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Black Mass\",\n        \"airing_time\": \"9:57 PM EDT\",\n        \"details_spans\": \"['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Amy\",\n        \"airing_time\": \"12:00 AM EDT\",\n        \"details_spans\": \"['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Yes Man\",\n        \"airing_time\": \"11:07 PM EDT\",\n        \"details_spans\": \"['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Verdict\",\n        \"airing_time\": \"12:52 AM EDT\",\n        \"details_spans\": \"['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Vanishing of Sidney Hall\",\n        \"airing_time\": \"3:01 AM EDT\",\n        \"details_spans\": \"['drama|', '2 hrs 1 min|', '2018|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Killing Me Softly\",\n        \"airing_time\": \"5:02 AM EDT\",\n        \"details_spans\": \"['drama|', 'R|', '1 hr 41 mins|', '2003|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Company You Keep\",\n        \"airing_time\": \"6:43 AM EDT\",\n        \"details_spans\": \"['suspense|', '2 hrs 2 mins|', '2013|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Don't Let Go\",\n        \"airing_time\": \"8:45 AM EDT\",\n        \"details_spans\": \"['horror|', '1 hr 44 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"The Dead Don't Die\",\n        \"airing_time\": \"10:29 AM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 45 mins|', '2019|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Strange Days\",\n        \"airing_time\": \"12:14 PM EDT\",\n        \"details_spans\": \"['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"She's Out of My League\",\n        \"airing_time\": \"2:40 PM EDT\",\n        \"details_spans\": \"['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Let Me In\",\n        \"airing_time\": \"4:25 PM EDT\",\n        \"details_spans\": \"['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Baby Mama\",\n        \"airing_time\": \"6:21 PM EDT\",\n        \"details_spans\": \"['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Office Space\",\n        \"airing_time\": \"8:00 PM EDT\",\n        \"details_spans\": \"['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Woman Walks Ahead\",\n        \"airing_time\": \"9:30 PM EDT\",\n        \"details_spans\": \"['drama|', '1 hr 43 mins|', '2018|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"I Love You, Beth Cooper\",\n        \"airing_time\": \"11:13 PM EDT\",\n        \"details_spans\": \"['comedy|', '1 hr 42 mins|', '2009|']\"\n    },\n    {\n        \"date\": \"March 23\",\n        \"name\": \"Elizabethtown\",\n        \"airing_time\": \"12:55 AM EDT\",\n        \"details_spans\": \"['comedy|', '2 hrs 4 mins|', '2005|']\"\n    }\n]"
  },
  {
    "path": "cookbook/function_call_force_schema.py",
    "content": "\nfrom duckduckgo_search import DDGS\nimport requests, os\nimport json\n\napi_key=os.environ[\"GROQ_API_KEY\"]\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\" \n\n# or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\n# proxy_url = \"http://localhost:8000/proxy/groq/v1/chat/completions\"\n\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n    \ndef duckduckgo_news(query, max_results=None):\n    \"\"\"\n    Use this function to get the latest news from DuckDuckGo.\n    \"\"\"    \n    with DDGS() as ddgs:\n        return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n    \"duckduckgo_news\": duckduckgo_news,\n}\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    # \"tool_choice\": \"auto\",\n    # \"tool_choice\": \"none\",\n    \"tool_choice\": {\"type\": \"function\", \"function\": {\"name\": \"duckduckgo_search\"}},\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_news\",\n                \"description\": \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n# Check if the request was successful\nif response.status_code == 200:\n    # Process the response data (if needed)\n    res = response.json()\n    message = res['choices'][0]['message']\n    tools_response_messages = []\n    if not message['content'] and 'tool_calls' in message:\n        for tool_call in message['tool_calls']:\n            tool_name = tool_call['function']['name']\n            tool_args = tool_call['function']['arguments']\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append({\n                \"role\": \"tool\", \"content\": json.dumps(tool_response)\n            })\n    \n        if tools_response_messages:\n            request['messages'] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res['choices'][0]['message']['content'])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message['content'])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "cookbook/function_call_force_tool_choice.py",
    "content": "from duckduckgo_search import DDGS\nimport requests, os\nimport json\n\napi_key=os.environ[\"GROQ_API_KEY\"]\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\n# proxy_url = \"https://funckycall.ai/proxy/groq/v1/chat/completions\"\nproxy_url = \"http://localhost:8000/proxy/groq/v1/chat/completions\"\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n    \ndef duckduckgo_news(query, max_results=None):\n    \"\"\"\n    Use this function to get the latest news from DuckDuckGo.\n    \"\"\"    \n    with DDGS() as ddgs:\n        return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n    \"duckduckgo_news\": duckduckgo_news,\n}\n\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, search in general and also search news, very short and concise.\",\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    # \"tool_choice\": \"auto\",\n    # \"tool_choice\": None,\n    \"tool_choice\": {\"type\": \"function\", \"function\": {\"name\": \"duckduckgo_search\"}},\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\"type\": \"string\"},\n                        \"max_results\": {\"type\": [\"number\", \"null\"]},\n                    },\n                },\n            },\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_news\",\n                \"description\": \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\"type\": \"string\"},\n                        \"max_results\": {\"type\": [\"number\", \"null\"]},\n                    },\n                },\n            },\n        },\n    ],\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request,\n)\n\n\n# Check if the request was successful\nif response.status_code == 200:\n    # Process the response data (if needed)\n    res = response.json()\n    message = res['choices'][0]['message']\n    tools_response_messages = []\n    if not message['content'] and 'tool_calls' in message:\n        for tool_call in message['tool_calls']:\n            tool_name = tool_call['function']['name']\n            tool_args = tool_call['function']['arguments']\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append({\n                \"role\": \"tool\", \"content\": json.dumps(tool_response)\n            })\n    \n        if tools_response_messages:\n            request['messages'] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res['choices'][0]['message']['content'])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message['content'])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "cookbook/function_call_ollama.py",
    "content": "\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\n\n# Tried the proxy with Ollama and it works great, meaning we can use it with any provider. But, you never get the speed of Groq ;)\nmy_ollama = OpenAILike(\n        model=\"gemma:7b\",\n        api_key=\"\",\n        base_url=\"http://localhost:11235/proxy/ollama/v1\"\n    )\nollama_assistant = Assistant(\n    llm=my_ollama,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\nollama_assistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)"
  },
  {
    "path": "cookbook/function_call_phidata.py",
    "content": "\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\nimport os, json\n\n\ngroq = OpenAILike(\n        model=\"mixtral-8x7b-32768\",\n        api_key=os.environ[\"GROQ_API_KEY\"],\n        base_url=\"https://api.groq.com/openai/v1\"\n    )\nassistant = Assistant(\n    llm=groq,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\n\n# If you run without a proxy, you will get a error, becuase Groq does not have a function to call\n# assistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n\nmy_groq = OpenAILike(\n        model=\"mixtral-8x7b-32768\", # or model=\"gemma-7b-it\",        \n        api_key=os.environ[\"GROQ_API_KEY\"],\n        base_url=\"https://groqcall.ai/proxy/groq/v1\" # or \"http://localhost:8000/proxy/groq/v1\" if running locally\n    )\nassistant = Assistant(\n    llm=my_groq,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\nassistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n\n\n\n\n"
  },
  {
    "path": "cookbook/function_call_vision.py",
    "content": "import requests, os\n\napi_key = os.environ[\"GROQ_API_KEY\"]\nheader = {\"Authorization\": f\"Bearer {api_key}\", \"Content-Type\": \"application/json\"}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"  # or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\nproxy_url = \"http://localhost:8000/proxy/groq/v1/chat/completions\"\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\",\n        },\n        {\n            \"role\": \"user\",\n            \"content\": [\n                {\"type\": \"text\", \"text\": \"What’s in this image?\"},\n                {\n                    \"type\": \"image_url\",\n                    \"image_url\": {\n                        \"url\": \"https://res.cloudinary.com/kidocode/image/upload/v1710690498/Gfp-wisconsin-madison-the-nature-boardwalk_m9jalr.jpg\"\n                    },\n                },\n            ],\n        },\n        {\n            \"role\": \"user\",\n            # \"content\": \"What’s in this image?\",\n            \"content\": \"Generate 3 keywords for the image description\",\n        },\n    ],\n    \"model\": \"mixtral-8x7b-32768\"\n}\n\nresponse = requests.post(proxy_url, headers=header, json=request)\n\n\nresponse.text\n\nprint(response.json()[\"choices\"][0][\"message\"][\"content\"])\n"
  },
  {
    "path": "cookbook/function_call_with_schema.py",
    "content": "\nfrom duckduckgo_search import DDGS\nimport requests, os\nimport json\n\napi_key=os.environ[\"GROQ_API_KEY\"]\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\" # or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\n\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n    \ndef duckduckgo_news(query, max_results=None):\n    \"\"\"\n    Use this function to get the latest news from DuckDuckGo.\n    \"\"\"    \n    with DDGS() as ddgs:\n        return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n    \"duckduckgo_news\": duckduckgo_news,\n}\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_news\",\n                \"description\": \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n# Check if the request was successful\nif response.status_code == 200:\n    # Process the response data (if needed)\n    res = response.json()\n    message = res['choices'][0]['message']\n    tools_response_messages = []\n    if not message['content'] and 'tool_calls' in message:\n        for tool_call in message['tool_calls']:\n            tool_name = tool_call['function']['name']\n            tool_args = tool_call['function']['arguments']\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append({\n                \"role\": \"tool\", \"content\": json.dumps(tool_response)\n            })\n    \n        if tools_response_messages:\n            request['messages'] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res['choices'][0]['message']['content'])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message['content'])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "cookbook/function_call_without_schema.py",
    "content": "import requests\nimport json\nimport os\n\napi_key=os.environ[\"GROQ_API_KEY\"],\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\" # or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\", \n            \"content\": \"What's happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.search\"\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\"\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n\nprint(response.json()[\"choices\"][0][\"message\"][\"content\"])"
  },
  {
    "path": "cookbook/functiona_call_groq_langchain.py",
    "content": "# pip install --upgrade --quiet langchain-groq tavily-python langchain langchainhub langchain-openai\n\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_groq import ChatGroq\nimport os\nfrom dotenv import load_dotenv\nload_dotenv()\nfrom langchain import hub\nfrom langchain.agents import create_openai_tools_agent\nfrom langchain_community.tools.tavily_search import TavilySearchResults, TavilyAnswer\nfrom langchain.agents import AgentExecutor\n\n# The following code raise an error.\nchat = ChatGroq(\n    temperature=0, \n    groq_api_key=os.environ[\"GROQ_API_KEY\"],\n    model_name=\"mixtral-8x7b-32768\", \n)\n\nprompt = hub.pull(\"hwchase17/openai-tools-agent\")\ntools = [TavilySearchResults(max_results=1)]\nagent = create_openai_tools_agent(chat, tools, prompt)\n\nagent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, stream_runnable = False)\nagent_executor.invoke({\"input\": \"What is Langchain?\"})\n\n\n# The following code works fine using GroqCall (Funckycall) proxy\nchat = ChatGroq(\n    temperature=0, \n    groq_api_key=os.environ[\"GROQ_API_KEY\"], \n    model_name=\"mixtral-8x7b-32768\", \n    groq_api_base= \"http://localhost:8000/proxy/groqchain\"\n    # groq_api_base= \"http://groqcall.ai/proxy/groqchain\"\n)\n\n# Example 1: Chat with tools\nprompt = hub.pull(\"hwchase17/openai-tools-agent\")\ntools = [TavilySearchResults(max_results=1)]\nagent = create_openai_tools_agent(chat, tools, prompt)\n\nagent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, stream_runnable = False)\nresult = agent_executor.invoke({\"input\": \"What is Langchain?\"})\nprint(result)\n\n# Example 1: Simple chat\nsystem = \"You are a helpful assistant.\"\nhuman = \"{text}\"\nprompt = ChatPromptTemplate.from_messages([(\"system\", system), (\"human\", human)])\n\nchain = prompt | chat\nresult = chain.invoke({\"text\": \"Explain the importance of low latency LLMs.\"})\nprint(result)\n"
  },
  {
    "path": "cookbook/resources.py",
    "content": "from phi.docker.app.postgres import PgVectorDb\nfrom phi.docker.resources import DockerResources\n\n# -*- PgVector2 running on port 5432:5432\nvector_db = PgVectorDb(\n    name=\"knowledge-db\",\n    pg_user=\"ai\",\n    pg_password=\"ai\",\n    pg_database=\"ai\",\n    host_port=5532,\n)\n\n# -*- DockerResources\ndev_docker_resources = DockerResources(apps=[vector_db])\n"
  },
  {
    "path": "examples/example_1.py",
    "content": "from phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\nimport os, json\n\ngroq = OpenAILike(\n        model=\"mixtral-8x7b-32768\",\n        api_key=os.environ[\"GROQ_API_KEY\"],\n        base_url=\"https://api.groq.com/openai/v1\"\n    )\nassistant = Assistant(\n    llm=groq,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\nassistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n\n\n\n\nmy_groq = OpenAILike(\n        # model=\"mixtral-8x7b-32768\",\n        model=\"gemma-7b-it\",\n        api_key=os.environ[\"GROQ_API_KEY\"],\n        base_url=\"https://groqcall.ai/proxy/groq/v1\"\n    )\nassistant = Assistant(\n    llm=my_groq,\n    tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n)\nassistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n\n\n\n\n# Tried the proxy with Ollama and it works great, meaning we can use it with any provider. But, you never get the speed of Groq ;)\n# my_ollama = OpenAILike(\n#         model=\"gemma:7b\",\n#         api_key=\"\",\n#         base_url=\"http://localhost:11235/proxy/ollama/v1\"\n#     )\n# ollama_assistant = Assistant(\n#     llm=my_ollama,\n#     tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n# )\n# ollama_assistant.print_response(\"Whats happening in France? Summarize top stories with sources, very short and concise.\", stream=False)"
  },
  {
    "path": "examples/example_2.py",
    "content": "from duckduckgo_search import DDGS\nimport requests, os\napi_key=os.environ[\"GROQ_API_KEY\"]\nimport json\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"\n\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]\n    \ndef duckduckgo_news(query, max_results=None):\n    \"\"\"\n    Use this function to get the latest news from DuckDuckGo.\n    \"\"\"    \n    with DDGS() as ddgs:\n        return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]\n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n    \"duckduckgo_news\": duckduckgo_news,\n}\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_news\",\n                \"description\": \"Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\n                            \"type\": \"string\"\n                        },\n                        \"max_results\": {\n                            \"type\": [\n                                \"number\",\n                                \"null\"\n                            ]\n                        }\n                    }\n                }\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n# Check if the request was successful\nif response.status_code == 200:\n    # Process the response data (if needed)\n    res = response.json()\n    message = res['choices'][0]['message']\n    tools_response_messages = []\n    if not message['content'] and 'tool_calls' in message:\n        for tool_call in message['tool_calls']:\n            tool_name = tool_call['function']['name']\n            tool_args = tool_call['function']['arguments']\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append({\n                \"role\": \"tool\", \"content\": json.dumps(tool_response)\n            })\n    \n        if tools_response_messages:\n            request['messages'] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res['choices'][0]['message']['content'])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message['content'])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "examples/example_3.py",
    "content": "from duckduckgo_search import DDGS\nimport requests, os\napi_key = os.environ[\"GROQ_API_KEY\"]\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"\n\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\",\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.\",\n        },\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.search\",\n            },\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\",\n            },\n        },\n    ],\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request,\n)\n\nif response.status_code == 200:\n    res = response.json()\n    print(res[\"choices\"][0][\"message\"][\"content\"])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "examples/example_4.py",
    "content": "from duckduckgo_search import DDGS\nimport requests, os, json\napi_key = os.environ[\"GROQ_API_KEY\"]\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\"\n\ndef duckduckgo_search(query, max_results=None):\n    \"\"\"\n    Use this function to search DuckDuckGo for a query.\n    \"\"\"\n    with DDGS() as ddgs:\n        return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]  \n\nfunction_map = {\n    \"duckduckgo_search\": duckduckgo_search,\n}\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\",\n        },\n        {\n            \"role\": \"user\",\n            \"content\": \"Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.\",\n        },\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduckgo_search\",\n                \"description\": \"Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.\",\n                \"parameters\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"query\": {\"type\": \"string\"},\n                        \"max_results\": {\"type\": [\"number\", \"null\"]},\n                    },\n                },\n            },\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\",\n            },\n        },\n    ],\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers= header,\n    json=request,\n)\n# Check if the request was successful\nif response.status_code == 200:\n    # Process the response data (if needed)\n    res = response.json()\n    message = res[\"choices\"][0][\"message\"]\n    tools_response_messages = []\n    if not message[\"content\"] and \"tool_calls\" in message:\n        if 'resolved' in res:\n            # Append resolved message to the tools response messages\n            tools_response_messages.extend(res['resolved'])\n\n        for tool_call in message[\"tool_calls\"]:\n            tool_name = tool_call[\"function\"][\"name\"]\n            tool_args = tool_call[\"function\"][\"arguments\"]\n            tool_args = json.loads(tool_args)\n            if tool_name not in function_map:\n                print(f\"Error: {tool_name} is not a valid function name.\")\n                continue\n            tool_func = function_map[tool_name]\n            tool_response = tool_func(**tool_args)\n            tools_response_messages.append(\n                {\"role\": \"tool\", \"content\": json.dumps(tool_response), \"name\": tool_name, \"tool_call_id\": tool_call[\"id\"]}\n            )\n\n        if tools_response_messages:\n            request[\"messages\"] += tools_response_messages\n            response = requests.post(\n                proxy_url,\n                headers=header,\n                json=request,\n            )\n            if response.status_code == 200:\n                res = response.json()\n                print(res[\"choices\"][0][\"message\"][\"content\"])\n            else:\n                print(\"Error:\", response.status_code, response.text)\n    else:\n        print(message[\"content\"])\nelse:\n    print(\"Error:\", response.status_code, response.text)\n"
  },
  {
    "path": "frontend/assets/README.md",
    "content": "# GroqCall.ai - Lightning-Fast LLM Function Calls\n\n[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing)\n[![Version](https://img.shields.io/badge/version-0.0.1-blue.svg)](https://github.com/unclecode/groqcall)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\nGroqCall is a proxy server that enables lightning-fast function calls for Groq's Language Processing Unit (LPU) and other AI providers. It simplifies the creation of AI assistants by offering a wide range of built-in functions hosted on the cloud.\n\n## Quickstart\n\n### Using the Pre-built Server\n\nTo quickly start using GroqCall without running it locally, make requests to one of the following base URLs:\n\n- Cloud: `https://groqcall.ai/proxy/groq/v1`\n- Local: `http://localhost:8000` (if running the proxy server locally)\n\n### Running the Proxy Locally\n\n1. Clone the repository:\n```\ngit clone https://github.com/unclecode/groqcall.git\ncd groqcall\n```\n\n2. Create and activate a virtual environment:\n```\npython -m venv venv\nsource venv/bin/activate\n```\n\n3. Install dependencies:\n```\npip install -r requirements.txt\n```\n\n4. Run the FastAPI server:\n```\n./venv/bin/uvicorn --app-dir app/ main:app --reload\n```\n\n## Examples\n\n### Using GroqCall with PhiData\n\n```python\nfrom phi.llm.openai.like import OpenAILike\nfrom phi.assistant import Assistant\nfrom phi.tools.duckduckgo import DuckDuckGo\n\nmy_groq = OpenAILike(\n    model=\"mixtral-8x7b-32768\",\n    api_key=\"YOUR_GROQ_API_KEY\",\n    base_url=\"https://groqcall.ai/proxy/groq/v1\"  # or \"http://localhost:8000/proxy/groq/v1\" if running locally\n)\n\nassistant = Assistant(\n    llm=my_groq,\n    tools=[DuckDuckGo()], \n    show_tool_calls=True, \n    markdown=True\n)\n\nassistant.print_response(\"What's happening in France? Summarize top stories with sources, very short and concise.\", stream=False)\n```\n\n### Using GroqCall with Requests\n\n#### FuncHub: Schema-less Function Calls\n\nGroqCall introduces FuncHub, which allows you to make function calls without passing the function schema. \n\n```python\nimport requests\n\napi_key = \"YOUR_GROQ_API_KEY\"\nheader = {\n    \"Authorization\": f\"Bearer {api_key}\",\n    \"Content-Type\": \"application/json\"\n}\n\nproxy_url = \"https://groqcall.ai/proxy/groq/v1/chat/completions\" # or \"http://localhost:8000/proxy/groq/v1/chat/completions\" if running locally\n\nrequest = {\n    \"messages\": [\n        {\n            \"role\": \"system\",\n            \"content\": \"YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n<instructions>\\n1. Use markdown to format your answers.\\n</instructions>\"\n        },\n        {\n            \"role\": \"user\", \n            \"content\": \"What's happening in France? Summarize top stories with sources, very short and concise.\"\n        }\n    ],\n    \"model\": \"mixtral-8x7b-32768\",\n    \"tool_choice\": \"auto\",\n    \"tools\": [\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.search\"\n            }\n        },\n        {\n            \"type\": \"function\",\n            \"function\": {\n                \"name\": \"duckduck.news\"\n            }\n        }\n    ]\n}\n\nresponse = requests.post(\n    proxy_url,\n    headers=header,\n    json=request\n)\n\nprint(response.json()[\"choices\"][0][\"message\"][\"content\"])\n```\n\n- If you notice, the function schema is not passed in the request. This is because GroqCall uses FuncHub to automatically detect and call the function based on the function name in the cloud, Therefore you dont't need to parse the first response, call the function, and pass again. Check \"functions\" folder to add your own functions. I will create more examples in the close future to explain how to add your own functions.\n\n#### Passing Function Schemas\n\nIf you prefer to pass your own function schemas, refer to the [Function Schema example](https://github.com/unclecode/groqcall/blob/main/cookbook/function_call_with_schema.py) in the cookbook.\n\n#### Rune proxy with Ollama locally\n\nFunction call proxy can be used with Ollama. You should first install Ollama and run it locally. Then refer to the [Ollama example](https://github.com/unclecode/groqcall/blob/main/cookbook/function_call_ollama.py) in the cookbook.\n\n## Cookbook\n\nExplore the [Cookbook](https://github.com/unclecode/groqcall/tree/main/cookbook) for more examples and use cases of GroqCall.\n\n## Motivation\n\nGroq is a startup that designs highly specialized processor chips aimed specifically at running inference on large language models. They've introduced what they call the Language Processing Unit (LPU), and the speed is astounding—capable of producing 500 to 800 tokens per second or more.\n\nAs an admirer of Groq and their community, I built this proxy to enable function calls using the OpenAI interface, allowing it to be called from any library. This engineering workaround has proven to be immensely useful in my company for various projects.\n\n## Contributing\n\nContributions are welcome! If you have ideas, suggestions, or would like to contribute to this project, please reach out to me on Twitter (X) @unclecode or via email at unclecode@kidocode.com.\n\nLet's collaborate and make this repository even more awesome! 🚀\n\n## License\n\nThis project is licensed under the Apache License 2.0. See [LICENSE](https://github.com/unclecode/groqcall/blob/main/LICENSE) for more information."
  },
  {
    "path": "frontend/assets/markdown.css",
    "content": "@media (prefers-color-scheme: dark) {\n    .markdown-body,\n    [data-theme=\"dark\"] {\n      /*dark*/\n      color-scheme: dark;\n      --color-prettylights-syntax-comment: #8b949e;\n      --color-prettylights-syntax-constant: #79c0ff;\n      --color-prettylights-syntax-entity: #d2a8ff;\n      --color-prettylights-syntax-storage-modifier-import: #c9d1d9;\n      --color-prettylights-syntax-entity-tag: #7ee787;\n      --color-prettylights-syntax-keyword: #ff7b72;\n      --color-prettylights-syntax-string: #a5d6ff;\n      --color-prettylights-syntax-variable: #ffa657;\n      --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;\n      --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;\n      --color-prettylights-syntax-invalid-illegal-bg: #8e1519;\n      --color-prettylights-syntax-carriage-return-text: #f0f6fc;\n      --color-prettylights-syntax-carriage-return-bg: #b62324;\n      --color-prettylights-syntax-string-regexp: #7ee787;\n      --color-prettylights-syntax-markup-list: #f2cc60;\n      --color-prettylights-syntax-markup-heading: #1f6feb;\n      --color-prettylights-syntax-markup-italic: #c9d1d9;\n      --color-prettylights-syntax-markup-bold: #c9d1d9;\n      --color-prettylights-syntax-markup-deleted-text: #ffdcd7;\n      --color-prettylights-syntax-markup-deleted-bg: #67060c;\n      --color-prettylights-syntax-markup-inserted-text: #aff5b4;\n      --color-prettylights-syntax-markup-inserted-bg: #033a16;\n      --color-prettylights-syntax-markup-changed-text: #ffdfb6;\n      --color-prettylights-syntax-markup-changed-bg: #5a1e02;\n      --color-prettylights-syntax-markup-ignored-text: #c9d1d9;\n      --color-prettylights-syntax-markup-ignored-bg: #1158c7;\n      --color-prettylights-syntax-meta-diff-range: #d2a8ff;\n      --color-prettylights-syntax-brackethighlighter-angle: #8b949e;\n      --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;\n      --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;\n      --color-fg-default: #e6edf3;\n      --color-fg-muted: #848d97;\n      --color-fg-subtle: #6e7681;\n      --color-canvas-default: #0d1117;\n      --color-canvas-subtle: #161b22;\n      --color-border-default: #30363d;\n      --color-border-muted: #21262d;\n      --color-neutral-muted: rgba(110,118,129,0.4);\n      --color-accent-fg: #2f81f7;\n      --color-accent-emphasis: #1f6feb;\n      --color-success-fg: #3fb950;\n      --color-success-emphasis: #238636;\n      --color-attention-fg: #d29922;\n      --color-attention-emphasis: #9e6a03;\n      --color-attention-subtle: rgba(187,128,9,0.15);\n      --color-danger-fg: #f85149;\n      --color-danger-emphasis: #da3633;\n      --color-done-fg: #a371f7;\n      --color-done-emphasis: #8957e5;\n    }\n  }\n  \n  @media (prefers-color-scheme: light) {\n    .markdown-body,\n    [data-theme=\"light\"] {\n      /*light*/\n      color-scheme: light;\n      --color-prettylights-syntax-comment: #57606a;\n      --color-prettylights-syntax-constant: #0550ae;\n      --color-prettylights-syntax-entity: #6639ba;\n      --color-prettylights-syntax-storage-modifier-import: #24292f;\n      --color-prettylights-syntax-entity-tag: #116329;\n      --color-prettylights-syntax-keyword: #cf222e;\n      --color-prettylights-syntax-string: #0a3069;\n      --color-prettylights-syntax-variable: #953800;\n      --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;\n      --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;\n      --color-prettylights-syntax-invalid-illegal-bg: #82071e;\n      --color-prettylights-syntax-carriage-return-text: #f6f8fa;\n      --color-prettylights-syntax-carriage-return-bg: #cf222e;\n      --color-prettylights-syntax-string-regexp: #116329;\n      --color-prettylights-syntax-markup-list: #3b2300;\n      --color-prettylights-syntax-markup-heading: #0550ae;\n      --color-prettylights-syntax-markup-italic: #24292f;\n      --color-prettylights-syntax-markup-bold: #24292f;\n      --color-prettylights-syntax-markup-deleted-text: #82071e;\n      --color-prettylights-syntax-markup-deleted-bg: #ffebe9;\n      --color-prettylights-syntax-markup-inserted-text: #116329;\n      --color-prettylights-syntax-markup-inserted-bg: #dafbe1;\n      --color-prettylights-syntax-markup-changed-text: #953800;\n      --color-prettylights-syntax-markup-changed-bg: #ffd8b5;\n      --color-prettylights-syntax-markup-ignored-text: #eaeef2;\n      --color-prettylights-syntax-markup-ignored-bg: #0550ae;\n      --color-prettylights-syntax-meta-diff-range: #8250df;\n      --color-prettylights-syntax-brackethighlighter-angle: #57606a;\n      --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;\n      --color-prettylights-syntax-constant-other-reference-link: #0a3069;\n      --color-fg-default: #1F2328;\n      --color-fg-muted: #656d76;\n      --color-fg-subtle: #6e7781;\n      --color-canvas-default: #ffffff;\n      --color-canvas-subtle: #f6f8fa;\n      --color-border-default: #d0d7de;\n      --color-border-muted: hsla(210,18%,87%,1);\n      --color-neutral-muted: rgba(175,184,193,0.2);\n      --color-accent-fg: #0969da;\n      --color-accent-emphasis: #0969da;\n      --color-success-fg: #1a7f37;\n      --color-success-emphasis: #1f883d;\n      --color-attention-fg: #9a6700;\n      --color-attention-emphasis: #9a6700;\n      --color-attention-subtle: #fff8c5;\n      --color-danger-fg: #d1242f;\n      --color-danger-emphasis: #cf222e;\n      --color-done-fg: #8250df;\n      --color-done-emphasis: #8250df;\n    }\n  }\n  \n  .markdown-body {\n    -ms-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%;\n    margin: 0;\n    color: var(--color-fg-default);\n    background-color: var(--color-canvas-default);\n    font-family: -apple-system,BlinkMacSystemFont,\"Segoe UI\",\"Noto Sans\",Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\";\n    font-size: 16px;\n    line-height: 1.5;\n    word-wrap: break-word;\n  }\n  \n  .markdown-body .octicon {\n    display: inline-block;\n    fill: currentColor;\n    vertical-align: text-bottom;\n  }\n  \n  .markdown-body h1:hover .anchor .octicon-link:before,\n  .markdown-body h2:hover .anchor .octicon-link:before,\n  .markdown-body h3:hover .anchor .octicon-link:before,\n  .markdown-body h4:hover .anchor .octicon-link:before,\n  .markdown-body h5:hover .anchor .octicon-link:before,\n  .markdown-body h6:hover .anchor .octicon-link:before {\n    width: 16px;\n    height: 16px;\n    content: ' ';\n    display: inline-block;\n    background-color: currentColor;\n    -webkit-mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\");\n    mask-image: url(\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>\");\n  }\n  \n  .markdown-body details,\n  .markdown-body figcaption,\n  .markdown-body figure {\n    display: block;\n  }\n  \n  .markdown-body summary {\n    display: list-item;\n  }\n  \n  .markdown-body [hidden] {\n    display: none !important;\n  }\n  \n  .markdown-body a {\n    background-color: transparent;\n    color: var(--color-accent-fg);\n    text-decoration: none;\n  }\n  \n  .markdown-body abbr[title] {\n    border-bottom: none;\n    -webkit-text-decoration: underline dotted;\n    text-decoration: underline dotted;\n  }\n  \n  .markdown-body b,\n  .markdown-body strong {\n    font-weight: var(--base-text-weight-semibold, 600);\n  }\n  \n  .markdown-body dfn {\n    font-style: italic;\n  }\n  \n  .markdown-body h1 {\n    margin: .67em 0;\n    font-weight: var(--base-text-weight-semibold, 600);\n    padding-bottom: .3em;\n    font-size: 2em;\n    border-bottom: 1px solid var(--color-border-muted);\n  }\n  \n  .markdown-body mark {\n    background-color: var(--color-attention-subtle);\n    color: var(--color-fg-default);\n  }\n  \n  .markdown-body small {\n    font-size: 90%;\n  }\n  \n  .markdown-body sub,\n  .markdown-body sup {\n    font-size: 75%;\n    line-height: 0;\n    position: relative;\n    vertical-align: baseline;\n  }\n  \n  .markdown-body sub {\n    bottom: -0.25em;\n  }\n  \n  .markdown-body sup {\n    top: -0.5em;\n  }\n  \n  .markdown-body img {\n    border-style: none;\n    max-width: 100%;\n    box-sizing: content-box;\n    background-color: var(--color-canvas-default);\n  }\n  \n  .markdown-body code,\n  .markdown-body kbd,\n  .markdown-body pre,\n  .markdown-body samp {\n    font-family: monospace;\n    font-size: 1em;\n  }\n  \n  .markdown-body figure {\n    margin: 1em 40px;\n  }\n  \n  .markdown-body hr {\n    box-sizing: content-box;\n    overflow: hidden;\n    background: transparent;\n    border-bottom: 1px solid var(--color-border-muted);\n    height: .25em;\n    padding: 0;\n    margin: 24px 0;\n    background-color: var(--color-border-default);\n    border: 0;\n  }\n  \n  .markdown-body input {\n    font: inherit;\n    margin: 0;\n    overflow: visible;\n    font-family: inherit;\n    font-size: inherit;\n    line-height: inherit;\n  }\n  \n  .markdown-body [type=button],\n  .markdown-body [type=reset],\n  .markdown-body [type=submit] {\n    -webkit-appearance: button;\n    appearance: button;\n  }\n  \n  .markdown-body [type=checkbox],\n  .markdown-body [type=radio] {\n    box-sizing: border-box;\n    padding: 0;\n  }\n  \n  .markdown-body [type=number]::-webkit-inner-spin-button,\n  .markdown-body [type=number]::-webkit-outer-spin-button {\n    height: auto;\n  }\n  \n  .markdown-body [type=search]::-webkit-search-cancel-button,\n  .markdown-body [type=search]::-webkit-search-decoration {\n    -webkit-appearance: none;\n    appearance: none;\n  }\n  \n  .markdown-body ::-webkit-input-placeholder {\n    color: inherit;\n    opacity: .54;\n  }\n  \n  .markdown-body ::-webkit-file-upload-button {\n    -webkit-appearance: button;\n    appearance: button;\n    font: inherit;\n  }\n  \n  .markdown-body a:hover {\n    text-decoration: underline;\n  }\n  \n  .markdown-body ::placeholder {\n    color: var(--color-fg-subtle);\n    opacity: 1;\n  }\n  \n  .markdown-body hr::before {\n    display: table;\n    content: \"\";\n  }\n  \n  .markdown-body hr::after {\n    display: table;\n    clear: both;\n    content: \"\";\n  }\n  \n  .markdown-body table {\n    border-spacing: 0;\n    border-collapse: collapse;\n    display: block;\n    width: max-content;\n    max-width: 100%;\n    overflow: auto;\n  }\n  \n  .markdown-body td,\n  .markdown-body th {\n    padding: 0;\n  }\n  \n  .markdown-body details summary {\n    cursor: pointer;\n  }\n  \n  .markdown-body details:not([open])>*:not(summary) {\n    display: none !important;\n  }\n  \n  .markdown-body a:focus,\n  .markdown-body [role=button]:focus,\n  .markdown-body input[type=radio]:focus,\n  .markdown-body input[type=checkbox]:focus {\n    outline: 2px solid var(--color-accent-fg);\n    outline-offset: -2px;\n    box-shadow: none;\n  }\n  \n  .markdown-body a:focus:not(:focus-visible),\n  .markdown-body [role=button]:focus:not(:focus-visible),\n  .markdown-body input[type=radio]:focus:not(:focus-visible),\n  .markdown-body input[type=checkbox]:focus:not(:focus-visible) {\n    outline: solid 1px transparent;\n  }\n  \n  .markdown-body a:focus-visible,\n  .markdown-body [role=button]:focus-visible,\n  .markdown-body input[type=radio]:focus-visible,\n  .markdown-body input[type=checkbox]:focus-visible {\n    outline: 2px solid var(--color-accent-fg);\n    outline-offset: -2px;\n    box-shadow: none;\n  }\n  \n  .markdown-body a:not([class]):focus,\n  .markdown-body a:not([class]):focus-visible,\n  .markdown-body input[type=radio]:focus,\n  .markdown-body input[type=radio]:focus-visible,\n  .markdown-body input[type=checkbox]:focus,\n  .markdown-body input[type=checkbox]:focus-visible {\n    outline-offset: 0;\n  }\n  \n  .markdown-body kbd {\n    display: inline-block;\n    padding: 3px 5px;\n    font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n    line-height: 10px;\n    color: var(--color-fg-default);\n    vertical-align: middle;\n    background-color: var(--color-canvas-subtle);\n    border: solid 1px var(--color-neutral-muted);\n    border-bottom-color: var(--color-neutral-muted);\n    border-radius: 6px;\n    box-shadow: inset 0 -1px 0 var(--color-neutral-muted);\n  }\n  \n  .markdown-body h1,\n  .markdown-body h2,\n  .markdown-body h3,\n  .markdown-body h4,\n  .markdown-body h5,\n  .markdown-body h6 {\n    margin-top: 24px;\n    margin-bottom: 16px;\n    font-weight: var(--base-text-weight-semibold, 600);\n    line-height: 1.25;\n  }\n  \n  .markdown-body h2 {\n    font-weight: var(--base-text-weight-semibold, 600);\n    padding-bottom: .3em;\n    font-size: 1.5em;\n    border-bottom: 1px solid var(--color-border-muted);\n  }\n  \n  .markdown-body h3 {\n    font-weight: var(--base-text-weight-semibold, 600);\n    font-size: 1.25em;\n  }\n  \n  .markdown-body h4 {\n    font-weight: var(--base-text-weight-semibold, 600);\n    font-size: 1em;\n  }\n  \n  .markdown-body h5 {\n    font-weight: var(--base-text-weight-semibold, 600);\n    font-size: .875em;\n  }\n  \n  .markdown-body h6 {\n    font-weight: var(--base-text-weight-semibold, 600);\n    font-size: .85em;\n    color: var(--color-fg-muted);\n  }\n  \n  .markdown-body p {\n    margin-top: 0;\n    margin-bottom: 10px;\n  }\n  \n  .markdown-body blockquote {\n    margin: 0;\n    padding: 0 1em;\n    color: var(--color-fg-muted);\n    border-left: .25em solid var(--color-border-default);\n  }\n  \n  .markdown-body ul,\n  .markdown-body ol {\n    margin-top: 0;\n    margin-bottom: 0;\n    padding-left: 2em;\n  }\n  \n  .markdown-body ol ol,\n  .markdown-body ul ol {\n    list-style-type: lower-roman;\n  }\n  \n  .markdown-body ul ul ol,\n  .markdown-body ul ol ol,\n  .markdown-body ol ul ol,\n  .markdown-body ol ol ol {\n    list-style-type: lower-alpha;\n  }\n  \n  .markdown-body dd {\n    margin-left: 0;\n  }\n  \n  .markdown-body tt,\n  .markdown-body code,\n  .markdown-body samp {\n    font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n    font-size: 12px;\n  }\n  \n  .markdown-body pre {\n    margin-top: 0;\n    margin-bottom: 0;\n    font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n    font-size: 12px;\n    word-wrap: normal;\n  }\n  \n  .markdown-body .octicon {\n    display: inline-block;\n    overflow: visible !important;\n    vertical-align: text-bottom;\n    fill: currentColor;\n  }\n  \n  .markdown-body input::-webkit-outer-spin-button,\n  .markdown-body input::-webkit-inner-spin-button {\n    margin: 0;\n    -webkit-appearance: none;\n    appearance: none;\n  }\n  \n  .markdown-body .mr-2 {\n    margin-right: var(--base-size-8, 8px) !important;\n  }\n  \n  .markdown-body::before {\n    display: table;\n    content: \"\";\n  }\n  \n  .markdown-body::after {\n    display: table;\n    clear: both;\n    content: \"\";\n  }\n  \n  .markdown-body>*:first-child {\n    margin-top: 0 !important;\n  }\n  \n  .markdown-body>*:last-child {\n    margin-bottom: 0 !important;\n  }\n  \n  .markdown-body a:not([href]) {\n    color: inherit;\n    text-decoration: none;\n  }\n  \n  .markdown-body .absent {\n    color: var(--color-danger-fg);\n  }\n  \n  .markdown-body .anchor {\n    float: left;\n    padding-right: 4px;\n    margin-left: -20px;\n    line-height: 1;\n  }\n  \n  .markdown-body .anchor:focus {\n    outline: none;\n  }\n  \n  .markdown-body p,\n  .markdown-body blockquote,\n  .markdown-body ul,\n  .markdown-body ol,\n  .markdown-body dl,\n  .markdown-body table,\n  .markdown-body pre,\n  .markdown-body details {\n    margin-top: 0;\n    margin-bottom: 16px;\n  }\n  \n  .markdown-body blockquote>:first-child {\n    margin-top: 0;\n  }\n  \n  .markdown-body blockquote>:last-child {\n    margin-bottom: 0;\n  }\n  \n  .markdown-body h1 .octicon-link,\n  .markdown-body h2 .octicon-link,\n  .markdown-body h3 .octicon-link,\n  .markdown-body h4 .octicon-link,\n  .markdown-body h5 .octicon-link,\n  .markdown-body h6 .octicon-link {\n    color: var(--color-fg-default);\n    vertical-align: middle;\n    visibility: hidden;\n  }\n  \n  .markdown-body h1:hover .anchor,\n  .markdown-body h2:hover .anchor,\n  .markdown-body h3:hover .anchor,\n  .markdown-body h4:hover .anchor,\n  .markdown-body h5:hover .anchor,\n  .markdown-body h6:hover .anchor {\n    text-decoration: none;\n  }\n  \n  .markdown-body h1:hover .anchor .octicon-link,\n  .markdown-body h2:hover .anchor .octicon-link,\n  .markdown-body h3:hover .anchor .octicon-link,\n  .markdown-body h4:hover .anchor .octicon-link,\n  .markdown-body h5:hover .anchor .octicon-link,\n  .markdown-body h6:hover .anchor .octicon-link {\n    visibility: visible;\n  }\n  \n  .markdown-body h1 tt,\n  .markdown-body h1 code,\n  .markdown-body h2 tt,\n  .markdown-body h2 code,\n  .markdown-body h3 tt,\n  .markdown-body h3 code,\n  .markdown-body h4 tt,\n  .markdown-body h4 code,\n  .markdown-body h5 tt,\n  .markdown-body h5 code,\n  .markdown-body h6 tt,\n  .markdown-body h6 code {\n    padding: 0 .2em;\n    font-size: inherit;\n  }\n  \n  .markdown-body summary h1,\n  .markdown-body summary h2,\n  .markdown-body summary h3,\n  .markdown-body summary h4,\n  .markdown-body summary h5,\n  .markdown-body summary h6 {\n    display: inline-block;\n  }\n  \n  .markdown-body summary h1 .anchor,\n  .markdown-body summary h2 .anchor,\n  .markdown-body summary h3 .anchor,\n  .markdown-body summary h4 .anchor,\n  .markdown-body summary h5 .anchor,\n  .markdown-body summary h6 .anchor {\n    margin-left: -40px;\n  }\n  \n  .markdown-body summary h1,\n  .markdown-body summary h2 {\n    padding-bottom: 0;\n    border-bottom: 0;\n  }\n  \n  .markdown-body ul.no-list,\n  .markdown-body ol.no-list {\n    padding: 0;\n    list-style-type: none;\n  }\n  \n  .markdown-body ol[type=\"a s\"] {\n    list-style-type: lower-alpha;\n  }\n  \n  .markdown-body ol[type=\"A s\"] {\n    list-style-type: upper-alpha;\n  }\n  \n  .markdown-body ol[type=\"i s\"] {\n    list-style-type: lower-roman;\n  }\n  \n  .markdown-body ol[type=\"I s\"] {\n    list-style-type: upper-roman;\n  }\n  \n  .markdown-body ol[type=\"1\"] {\n    list-style-type: decimal;\n  }\n  \n  .markdown-body div>ol:not([type]) {\n    list-style-type: decimal;\n  }\n  \n  .markdown-body ul ul,\n  .markdown-body ul ol,\n  .markdown-body ol ol,\n  .markdown-body ol ul {\n    margin-top: 0;\n    margin-bottom: 0;\n  }\n  \n  .markdown-body li>p {\n    margin-top: 16px;\n  }\n  \n  .markdown-body li+li {\n    margin-top: .25em;\n  }\n  \n  .markdown-body dl {\n    padding: 0;\n  }\n  \n  .markdown-body dl dt {\n    padding: 0;\n    margin-top: 16px;\n    font-size: 1em;\n    font-style: italic;\n    font-weight: var(--base-text-weight-semibold, 600);\n  }\n  \n  .markdown-body dl dd {\n    padding: 0 16px;\n    margin-bottom: 16px;\n  }\n  \n  .markdown-body table th {\n    font-weight: var(--base-text-weight-semibold, 600);\n  }\n  \n  .markdown-body table th,\n  .markdown-body table td {\n    padding: 6px 13px;\n    border: 1px solid var(--color-border-default);\n  }\n  \n  .markdown-body table td>:last-child {\n    margin-bottom: 0;\n  }\n  \n  .markdown-body table tr {\n    background-color: var(--color-canvas-default);\n    border-top: 1px solid var(--color-border-muted);\n  }\n  \n  .markdown-body table tr:nth-child(2n) {\n    background-color: var(--color-canvas-subtle);\n  }\n  \n  .markdown-body table img {\n    background-color: transparent;\n  }\n  \n  .markdown-body img[align=right] {\n    padding-left: 20px;\n  }\n  \n  .markdown-body img[align=left] {\n    padding-right: 20px;\n  }\n  \n  .markdown-body .emoji {\n    max-width: none;\n    vertical-align: text-top;\n    background-color: transparent;\n  }\n  \n  .markdown-body span.frame {\n    display: block;\n    overflow: hidden;\n  }\n  \n  .markdown-body span.frame>span {\n    display: block;\n    float: left;\n    width: auto;\n    padding: 7px;\n    margin: 13px 0 0;\n    overflow: hidden;\n    border: 1px solid var(--color-border-default);\n  }\n  \n  .markdown-body span.frame span img {\n    display: block;\n    float: left;\n  }\n  \n  .markdown-body span.frame span span {\n    display: block;\n    padding: 5px 0 0;\n    clear: both;\n    color: var(--color-fg-default);\n  }\n  \n  .markdown-body span.align-center {\n    display: block;\n    overflow: hidden;\n    clear: both;\n  }\n  \n  .markdown-body span.align-center>span {\n    display: block;\n    margin: 13px auto 0;\n    overflow: hidden;\n    text-align: center;\n  }\n  \n  .markdown-body span.align-center span img {\n    margin: 0 auto;\n    text-align: center;\n  }\n  \n  .markdown-body span.align-right {\n    display: block;\n    overflow: hidden;\n    clear: both;\n  }\n  \n  .markdown-body span.align-right>span {\n    display: block;\n    margin: 13px 0 0;\n    overflow: hidden;\n    text-align: right;\n  }\n  \n  .markdown-body span.align-right span img {\n    margin: 0;\n    text-align: right;\n  }\n  \n  .markdown-body span.float-left {\n    display: block;\n    float: left;\n    margin-right: 13px;\n    overflow: hidden;\n  }\n  \n  .markdown-body span.float-left span {\n    margin: 13px 0 0;\n  }\n  \n  .markdown-body span.float-right {\n    display: block;\n    float: right;\n    margin-left: 13px;\n    overflow: hidden;\n  }\n  \n  .markdown-body span.float-right>span {\n    display: block;\n    margin: 13px auto 0;\n    overflow: hidden;\n    text-align: right;\n  }\n  \n  .markdown-body code,\n  .markdown-body tt {\n    padding: .2em .4em;\n    margin: 0;\n    font-size: 85%;\n    white-space: break-spaces;\n    background-color: var(--color-neutral-muted);\n    border-radius: 6px;\n  }\n  \n  .markdown-body code br,\n  .markdown-body tt br {\n    display: none;\n  }\n  \n  .markdown-body del code {\n    text-decoration: inherit;\n  }\n  \n  .markdown-body samp {\n    font-size: 85%;\n  }\n  \n  .markdown-body pre code {\n    font-size: 100%;\n  }\n  \n  .markdown-body pre>code {\n    padding: 0;\n    margin: 0;\n    word-break: normal;\n    white-space: pre;\n    background: transparent;\n    border: 0;\n  }\n  \n  .markdown-body .highlight {\n    margin-bottom: 16px;\n  }\n  \n  .markdown-body .highlight pre {\n    margin-bottom: 0;\n    word-break: normal;\n  }\n  \n  .markdown-body .highlight pre,\n  .markdown-body pre {\n    padding: 16px;\n    overflow: auto;\n    font-size: 85%;\n    line-height: 1.45;\n    color: var(--color-fg-default);\n    background-color: var(--color-canvas-subtle);\n    border-radius: 6px;\n  }\n  \n  .markdown-body pre code,\n  .markdown-body pre tt {\n    display: inline;\n    max-width: auto;\n    padding: 0;\n    margin: 0;\n    overflow: visible;\n    line-height: inherit;\n    word-wrap: normal;\n    background-color: transparent;\n    border: 0;\n  }\n  \n  .markdown-body .csv-data td,\n  .markdown-body .csv-data th {\n    padding: 5px;\n    overflow: hidden;\n    font-size: 12px;\n    line-height: 1;\n    text-align: left;\n    white-space: nowrap;\n  }\n  \n  .markdown-body .csv-data .blob-num {\n    padding: 10px 8px 9px;\n    text-align: right;\n    background: var(--color-canvas-default);\n    border: 0;\n  }\n  \n  .markdown-body .csv-data tr {\n    border-top: 0;\n  }\n  \n  .markdown-body .csv-data th {\n    font-weight: var(--base-text-weight-semibold, 600);\n    background: var(--color-canvas-subtle);\n    border-top: 0;\n  }\n  \n  .markdown-body [data-footnote-ref]::before {\n    content: \"[\";\n  }\n  \n  .markdown-body [data-footnote-ref]::after {\n    content: \"]\";\n  }\n  \n  .markdown-body .footnotes {\n    font-size: 12px;\n    color: var(--color-fg-muted);\n    border-top: 1px solid var(--color-border-default);\n  }\n  \n  .markdown-body .footnotes ol {\n    padding-left: 16px;\n  }\n  \n  .markdown-body .footnotes ol ul {\n    display: inline-block;\n    padding-left: 16px;\n    margin-top: 16px;\n  }\n  \n  .markdown-body .footnotes li {\n    position: relative;\n  }\n  \n  .markdown-body .footnotes li:target::before {\n    position: absolute;\n    top: -8px;\n    right: -8px;\n    bottom: -8px;\n    left: -24px;\n    pointer-events: none;\n    content: \"\";\n    border: 2px solid var(--color-accent-emphasis);\n    border-radius: 6px;\n  }\n  \n  .markdown-body .footnotes li:target {\n    color: var(--color-fg-default);\n  }\n  \n  .markdown-body .footnotes .data-footnote-backref g-emoji {\n    font-family: monospace;\n  }\n  \n  .markdown-body .pl-c {\n    color: var(--color-prettylights-syntax-comment);\n  }\n  \n  .markdown-body .pl-c1,\n  .markdown-body .pl-s .pl-v {\n    color: var(--color-prettylights-syntax-constant);\n  }\n  \n  .markdown-body .pl-e,\n  .markdown-body .pl-en {\n    color: var(--color-prettylights-syntax-entity);\n  }\n  \n  .markdown-body .pl-smi,\n  .markdown-body .pl-s .pl-s1 {\n    color: var(--color-prettylights-syntax-storage-modifier-import);\n  }\n  \n  .markdown-body .pl-ent {\n    color: var(--color-prettylights-syntax-entity-tag);\n  }\n  \n  .markdown-body .pl-k {\n    color: var(--color-prettylights-syntax-keyword);\n  }\n  \n  .markdown-body .pl-s,\n  .markdown-body .pl-pds,\n  .markdown-body .pl-s .pl-pse .pl-s1,\n  .markdown-body .pl-sr,\n  .markdown-body .pl-sr .pl-cce,\n  .markdown-body .pl-sr .pl-sre,\n  .markdown-body .pl-sr .pl-sra {\n    color: var(--color-prettylights-syntax-string);\n  }\n  \n  .markdown-body .pl-v,\n  .markdown-body .pl-smw {\n    color: var(--color-prettylights-syntax-variable);\n  }\n  \n  .markdown-body .pl-bu {\n    color: var(--color-prettylights-syntax-brackethighlighter-unmatched);\n  }\n  \n  .markdown-body .pl-ii {\n    color: var(--color-prettylights-syntax-invalid-illegal-text);\n    background-color: var(--color-prettylights-syntax-invalid-illegal-bg);\n  }\n  \n  .markdown-body .pl-c2 {\n    color: var(--color-prettylights-syntax-carriage-return-text);\n    background-color: var(--color-prettylights-syntax-carriage-return-bg);\n  }\n  \n  .markdown-body .pl-sr .pl-cce {\n    font-weight: bold;\n    color: var(--color-prettylights-syntax-string-regexp);\n  }\n  \n  .markdown-body .pl-ml {\n    color: var(--color-prettylights-syntax-markup-list);\n  }\n  \n  .markdown-body .pl-mh,\n  .markdown-body .pl-mh .pl-en,\n  .markdown-body .pl-ms {\n    font-weight: bold;\n    color: var(--color-prettylights-syntax-markup-heading);\n  }\n  \n  .markdown-body .pl-mi {\n    font-style: italic;\n    color: var(--color-prettylights-syntax-markup-italic);\n  }\n  \n  .markdown-body .pl-mb {\n    font-weight: bold;\n    color: var(--color-prettylights-syntax-markup-bold);\n  }\n  \n  .markdown-body .pl-md {\n    color: var(--color-prettylights-syntax-markup-deleted-text);\n    background-color: var(--color-prettylights-syntax-markup-deleted-bg);\n  }\n  \n  .markdown-body .pl-mi1 {\n    color: var(--color-prettylights-syntax-markup-inserted-text);\n    background-color: var(--color-prettylights-syntax-markup-inserted-bg);\n  }\n  \n  .markdown-body .pl-mc {\n    color: var(--color-prettylights-syntax-markup-changed-text);\n    background-color: var(--color-prettylights-syntax-markup-changed-bg);\n  }\n  \n  .markdown-body .pl-mi2 {\n    color: var(--color-prettylights-syntax-markup-ignored-text);\n    background-color: var(--color-prettylights-syntax-markup-ignored-bg);\n  }\n  \n  .markdown-body .pl-mdr {\n    font-weight: bold;\n    color: var(--color-prettylights-syntax-meta-diff-range);\n  }\n  \n  .markdown-body .pl-ba {\n    color: var(--color-prettylights-syntax-brackethighlighter-angle);\n  }\n  \n  .markdown-body .pl-sg {\n    color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);\n  }\n  \n  .markdown-body .pl-corl {\n    text-decoration: underline;\n    color: var(--color-prettylights-syntax-constant-other-reference-link);\n  }\n  \n  .markdown-body g-emoji {\n    display: inline-block;\n    min-width: 1ch;\n    font-family: \"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\";\n    font-size: 1em;\n    font-style: normal !important;\n    font-weight: var(--base-text-weight-normal, 400);\n    line-height: 1;\n    vertical-align: -0.075em;\n  }\n  \n  .markdown-body g-emoji img {\n    width: 1em;\n    height: 1em;\n  }\n  \n  .markdown-body .task-list-item {\n    list-style-type: none;\n  }\n  \n  .markdown-body .task-list-item label {\n    font-weight: var(--base-text-weight-normal, 400);\n  }\n  \n  .markdown-body .task-list-item.enabled label {\n    cursor: pointer;\n  }\n  \n  .markdown-body .task-list-item+.task-list-item {\n    margin-top: 4px;\n  }\n  \n  .markdown-body .task-list-item .handle {\n    display: none;\n  }\n  \n  .markdown-body .task-list-item-checkbox {\n    margin: 0 .2em .25em -1.4em;\n    vertical-align: middle;\n  }\n  \n  .markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {\n    margin: 0 -1.6em .25em .2em;\n  }\n  \n  .markdown-body .contains-task-list {\n    position: relative;\n  }\n  \n  .markdown-body .contains-task-list:hover .task-list-item-convert-container,\n  .markdown-body .contains-task-list:focus-within .task-list-item-convert-container {\n    display: block;\n    width: auto;\n    height: 24px;\n    overflow: visible;\n    clip: auto;\n  }\n  \n  .markdown-body ::-webkit-calendar-picker-indicator {\n    filter: invert(50%);\n  }\n  \n  .markdown-body .markdown-alert {\n    padding: var(--base-size-8) var(--base-size-16);\n    margin-bottom: 16px;\n    color: inherit;\n    border-left: .25em solid var(--color-border-default);\n  }\n  \n  .markdown-body .markdown-alert>:first-child {\n    margin-top: 0;\n  }\n  \n  .markdown-body .markdown-alert>:last-child {\n    margin-bottom: 0;\n  }\n  \n  .markdown-body .markdown-alert .markdown-alert-title {\n    display: flex;\n    font-weight: var(--base-text-weight-medium, 500);\n    align-items: center;\n    line-height: 1;\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-note {\n    border-left-color: var(--color-accent-emphasis);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title {\n    color: var(--color-accent-fg);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-important {\n    border-left-color: var(--color-done-emphasis);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title {\n    color: var(--color-done-fg);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-warning {\n    border-left-color: var(--color-attention-emphasis);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title {\n    color: var(--color-attention-fg);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-tip {\n    border-left-color: var(--color-success-emphasis);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title {\n    color: var(--color-success-fg);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-caution {\n    border-left-color: var(--color-danger-emphasis);\n  }\n  \n  .markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title {\n    color: var(--color-danger-fg);\n  }\n  "
  },
  {
    "path": "frontend/assets/style.css",
    "content": "@import \"tailwindcss/base\";\n@import \"tailwindcss/components\";\n@import \"tailwindcss/utilities\";"
  },
  {
    "path": "frontend/pages/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n        <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n        <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap\" rel=\"stylesheet\" />\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>GroqCall</title>\n        <link href=\"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css\" rel=\"stylesheet\" />\n        <link href=\"/static/markdown.css\" rel=\"stylesheet\" />\n        <style>\n            html,\n            body {\n                font-family: \"Inter\", sans-serif;\n                font-optical-sizing: auto;\n                font-style: normal;\n                font-variation-settings: \"slnt\" 0;\n            }\n            pre {\n                overflow: auto;\n            }\n            h2 {\n                margin: 2rem 0 !important;\n            }\n            .markdown-body p:first-of-type {\n                display: flex !important;\n                gap: 1rem !important;\n            }\n        </style>\n    </head>\n    <body class=\"bg-black text-gray-200\">\n        <main>\n            <div class=\"container mx-auto px-20 py-8\">\n                <div id=\"readme-container\" class=\"bg-gray-900 text-white px-10 py-12 rounded-lg shadow-lg markdown-body\">\n                    <div class = \"flex justify-center items-center gap-4\">\n                        <div class=\"w-16 h-16 border-t-4 border-blue-500 border-solid rounded-full animate-spin\"></div>\n                        <div>Loading</div>                        \n                    </div>\n                </div>\n            </div>\n        </main>\n        <script src=\"https://cdn.jsdelivr.net/npm/marked/marked.min.js\"></script>\n        <script>\n            const repoUrl = \"https://api.github.com/repos/unclecode/groqcall/contents/README.md\";\n            //const repoUrl = \"http://0.0.0.0:8000/static/README.md\";\n\n            // Function to convert markdown to HTML\n            function markdownToHtml(markdown) {\n                const converter = new showdown.Converter();\n                return converter.makeHtml(markdown);\n            }\n\n            // Function to fetch README content from GitHub\n            async function fetchReadmeContent() {\n                try {\n                    const response = await fetch(repoUrl);\n                    //const data = await response.text()\n                    //const readmeContent = data\n                    //return readmeContent; \n\n                    const data = await response.json();\n                    const readmeContent = atob(data.content);\n                    return readmeContent;\n                } catch (error) {\n                    console.error(\"Error fetching README content:\", error);\n                    return null;\n                }\n            }\n\n            // Function to render README content\n            async function renderReadme() {\n                const readmeContainer = document.getElementById(\"readme-container\");\n\n                // Check if README content is already cached\n                const cachedReadmeContent = localStorage.getItem(\"readmeContent\");\n\n                if (false && cachedReadmeContent) {\n                    // Use cached content\n                    readmeContainer.innerHTML = markdownToHtml(cachedReadmeContent);\n                } else {\n                    // Fetch README content from GitHub\n                    const readmeContent = await fetchReadmeContent();\n\n                    if (readmeContent) {\n                        // Cache the README content\n                        localStorage.setItem(\"readmeContent\", readmeContent);\n\n                        // Convert markdown to HTML and set the innerHTML\n                        readmeContainer.innerHTML =marked.parse(readmeContent);\n                    } else {\n                        readmeContainer.innerHTML = \"Failed to load README content.\";\n                    }\n                }\n            }\n\n            // Render the README content\n            renderReadme();\n        </script>\n    </body>\n</html>\n"
  },
  {
    "path": "frontend/pages/index_old.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n        <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n        <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap\" rel=\"stylesheet\" />\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <title>GroqCall</title>\n        <link href=\"https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css\" rel=\"stylesheet\" />\n        <link\n            rel=\"stylesheet\"\n            href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism-tomorrow.min.css\"\n        />\n        <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/prism.min.js\"></script>\n        <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/components/prism-python.min.js\"></script>\n        <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/components/prism-bash.min.js\"></script>\n        <style>\n            html,\n            body {\n                font-family: \"Inter\", sans-serif;\n                font-optical-sizing: auto;\n                font-style: normal;\n                font-variation-settings: \"slnt\" 0;\n            }\n            pre {\n                overflow: auto;\n                background-color: #1f2937;\n                font-family: monospace;\n                font-size: 0.8rem;\n                padding: 1rem;\n                border-radius: 10px;\n                margin: 1rem 0;\n                color: #d1d5db;\n                \n            }\n            h2 {\n                margin: 2rem 0 !important;\n                font-size: 2rem;\n            }\n            h3 {\n                margin: 1rem 0 !important;\n                font-size: 1.5rem;\n            }\n        </style>\n    </head>\n    <body class=\"bg-black text-gray-200\">\n        <main>\n            <div class=\"container mx-auto px-20 py-8\">\n                <div class=\"bg-gray-900 text-white px-10 py-12 rounded-lg shadow-lg\">\n                    <h1 class=\"text-4xl font-bold mb-6\">GroqCall.ai</h1>\n                    <p class=\"mb-6\">\n                        <a\n                            href=\"https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing\"\n                            class=\"inline-block mr-4\"\n                        >\n                            <img\n                                src=\"https://colab.research.google.com/assets/colab-badge.svg\"\n                                alt=\"Open In Colab\"\n                                class=\"h-6\"\n                            />\n                        </a>\n                        <a href=\"https://github.com/unclecode/groqcall\" class=\"inline-block mr-4\">\n                            <img src=\"https://img.shields.io/badge/version-0.0.1-blue.svg\" alt=\"Version\" class=\"h-6\" />\n                        </a>\n                        <a href=\"https://opensource.org/licenses/MIT\" class=\"inline-block\">\n                            <img\n                                src=\"https://img.shields.io/badge/License-MIT-yellow.svg\"\n                                alt=\"License: MIT\"\n                                class=\"h-6\"\n                            />\n                        </a>\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        GroqCall is a proxy server that provides function calls for Groq's lightning-fast Language\n                        Processing Unit (LPU) and other AI providers. Additionally, the upcoming FuncyHub will offer a\n                        wide range of built-in functions, hosted on the cloud, making it easier to create AI assistants\n                        without the need to maintain function schemas in the codebase or execute them through multiple\n                        calls.\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        Check github repo for more info:\n                        <a\n                            href=\"https://github.com/unclecode/groqcall\n                        \"\n                            class=\"text-blue-500 underline\"\n                            >https://github.com/unclecode/groqcall</a\n                        >\n                    </p>\n                    <h2 class=\"text-3xl font-bold mb-4\">Motivation 🚀</h2>\n                    <p class=\"text-lg mb-8\">\n                        Groq is a startup that designs highly specialized processor chips aimed specifically at running\n                        inference on large language models. They've introduced what they call the Language Processing\n                        Unit (LPU), and the speed is astounding—capable of producing 500 to 800 tokens per second or\n                        more. I've become a big fan of Groq and their community;\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        I admire what they're doing. It feels like after discovering electricity, the next challenge is\n                        moving it around quickly and efficiently. Groq is doing just that for Artificial Intelligence,\n                        making it easily accessible everywhere. They've opened up their API to the cloud, but as of now,\n                        they lack a function call capability.\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        Unable to wait for this feature, I built a proxy that enables function calls using the OpenAI\n                        interface, allowing it to be called from any library. This engineering workaround has proven to\n                        be immensely useful in my company for various projects. Here's the link to the GitHub repository\n                        where you can explore and play around with it. I've included some examples in this collaboration\n                        for you to check out.\n                    </p>\n                    <img\n                        src=\"https://res.cloudinary.com/kidocode/image/upload/v1710148127/GroqChip-1-Die_lgi95d.jpg\"\n                        alt=\"Groq Chip\"\n                        class=\"w-48 mb-8\"\n                    />\n                    <img\n                        src=\"https://res.cloudinary.com/kidocode/image/upload/v1710142103/Stack_PBG_White_n6qdbj.svg\"\n                        alt=\"Powered by Groq\"\n                        class=\"w-48 mb-8\"\n                    />\n\n                    <h2 class=\"text-3xl font-bold mb-4\">Running the Proxy Locally 🖥️</h2>\n                    <p class=\"text-lg mb-4\">To run this proxy locally on your own machine, follow these steps:</p>\n                    <ol class=\"list-decimal list-inside mb-8\">\n                        <li>Clone the GitHub repository:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4 mb-4\"\n                        ><code class = \"language-bash\">git clone https://github.com/unclecode/groqcall.git</code></pre>\n                        <li>Navigate to the project directory:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4 mb-4\"\n                        ><code class = \"language-bash\">cd groqcall</code></pre>\n                        <li>Create a virtual environment:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4 mb-4\"\n                        ><code class = \"language-bash\">python -m venv venv</code></pre>\n                        <li>Activate the virtual environment:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4 mb-4\"\n                        ><code class = \"language-bash\">source venv/bin/activate</code></pre>\n                        <li>Install the required libraries:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4 mb-4\"\n                        ><code class = \"language-bash\">pip install -r requirements.txt</code></pre>\n                        <li>Run the FastAPI server:</li>\n                        <pre\n                            class=\"bg-gray-800 rounded p-4\"\n                        ><code class = \"language-bash\">.venv/bin/uvicorn --app-dir app/ main:app --reload</code></pre>\n                    </ol>\n                    <h2 class=\"text-3xl font-bold mb-4\">Using the Pre-built Server 🌐</h2>\n                    <p class=\"text-lg mb-8\">\n                        For your convenience, I have already set up a server that you can use temporarily. This allows\n                        you to quickly start using the proxy without having to run it locally.\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        To use the pre-built server, simply make requests to the following base URL:\n                        <code class=\"bg-gray-800 rounded px-2 py-1\">https://groqcall.ai/proxy/groq/v1</code>\n                    </p>\n\n                    <h2 class=\"text-3xl font-bold mb-4\">Exploring GroqCall.ai 🚀</h2>\n                    <p class=\"text-lg mb-8\">\n                        This README is organized into three main sections, each showcasing different aspects of\n                        GroqCall.ai:\n                    </p>\n                    <ul class=\"list-disc list-inside mb-8\">\n                        <!-- set line height to 1.5 -->\n                        <li class=\"mb-4 leading-7\">\n                            <strong>Sending POST Requests</strong>: Here, I explore the functionality of sending direct\n                            POST requests to LLMs using GroqCall.ai. This section highlights the flexibility and\n                            control offered by the library when interacting with LLMs.\n                        </li>\n                        <li class=\"mb-4 leading-7\">\n                            <strong>FunckyHub</strong>: The third section introduces the concept of FunckyHub, a useful\n                            feature that simplifies the process of executing functions. With FunckyHub, there is no need\n                            to send the function JSON schema explicitly, as the functions are already hosted on the\n                            proxy server. This approach streamlines the workflow, allowing developers to obtain results\n                            with a single call without having to handle function calls in a production server.\n                        </li>\n                        <li class=\"mb-4 leading-7\">\n                            <strong>Using GroqCall with PhiData</strong>: In this section, I demonstrate how\n                            GroqCall.ai can be seamlessly integrated with other libraries such as my favorite one, the\n                            PhiData library, leveraging its built-in tools to connect to LLMs and perform external tool\n                            requests.\n                        </li>\n                    </ul>\n                    <pre\n                        class=\"rounded p-4 mb-4\"\n                    ><code class=\"language-bash\"># The following libraries are optional if you&#39;re interested in using PhiData or managing your tools on the client side.\n    !pip install phidata &gt; /dev/null\n    !pip install openai &gt; /dev/null\n    !pip install duckduckgo-search &gt; /dev/null\n    </code></pre>\n                    <h2 class=\"text-3xl font-bold mb-4\">Sending POST request, with full functions implementation</h2>\n                    <p class=\"text-lg mb-8\">\n                        Check out the example file\n                        <a href=\"examples/example_2.py\" class=\"text-blue-500 underline\">example_2.py</a>\n                        for a full implementation of the following code.\n                    </p>\n                    <pre class=\"rounded p-4 mb-4\"><code class=\"language-python\">from duckduckgo_search import DDGS\n    import requests, os\n    import json\n    \n    # Here you pass your own GROQ API key\n    api_key=userdata.get(&quot;GROQ_API_KEY&quot;)\n    header = {\n        &quot;Authorization&quot;: f&quot;Bearer {api_key}&quot;,\n        &quot;Content-Type&quot;: &quot;application/json&quot;\n    }\n    proxy_url = &quot;https://groqcall.ai/proxy/groq/v1/chat/completions&quot;\n    \n    \n    def duckduckgo_search(query, max_results=None):\n        &quot;&quot;&quot;\n        Use this function to search DuckDuckGo for a query.\n        &quot;&quot;&quot;\n        with DDGS() as ddgs:\n            return [r for r in ddgs.text(query, safesearch=&#39;off&#39;, max_results=max_results)]\n    \n    def duckduckgo_news(query, max_results=None):\n        &quot;&quot;&quot;\n        Use this function to get the latest news from DuckDuckGo.\n        &quot;&quot;&quot;\n        with DDGS() as ddgs:\n            return [r for r in ddgs.news(query, safesearch=&#39;off&#39;, max_results=max_results)]\n    \n    function_map = {\n        &quot;duckduckgo_search&quot;: duckduckgo_search,\n        &quot;duckduckgo_news&quot;: duckduckgo_news,\n    }\n    \n    request = {\n        &quot;messages&quot;: [\n            {\n                &quot;role&quot;: &quot;system&quot;,\n                &quot;content&quot;: &quot;YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n&lt;instructions&gt;\\n1. Use markdown to format your answers.\\n&lt;/instructions&gt;&quot;\n            },\n            {\n                &quot;role&quot;: &quot;user&quot;,\n                &quot;content&quot;: &quot;Whats happening in France? Summarize top stories with sources, very short and concise.&quot;\n            }\n        ],\n        &quot;model&quot;: &quot;mixtral-8x7b-32768&quot;,\n        &quot;tool_choice&quot;: &quot;auto&quot;,\n        &quot;tools&quot;: [\n            {\n                &quot;type&quot;: &quot;function&quot;,\n                &quot;function&quot;: {\n                    &quot;name&quot;: &quot;duckduckgo_search&quot;,\n                    &quot;description&quot;: &quot;Use this function to search DuckDuckGo for a query.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The result from DuckDuckGo.&quot;,\n                    &quot;parameters&quot;: {\n                        &quot;type&quot;: &quot;object&quot;,\n                        &quot;properties&quot;: {\n                            &quot;query&quot;: {\n                                &quot;type&quot;: &quot;string&quot;\n                            },\n                            &quot;max_results&quot;: {\n                                &quot;type&quot;: [\n                                    &quot;number&quot;,\n                                    &quot;null&quot;\n                                ]\n                            }\n                        }\n                    }\n                }\n            },\n            {\n                &quot;type&quot;: &quot;function&quot;,\n                &quot;function&quot;: {\n                    &quot;name&quot;: &quot;duckduckgo_news&quot;,\n                    &quot;description&quot;: &quot;Use this function to get the latest news from DuckDuckGo.\\n\\nArgs:\\n    query(str): The query to search for.\\n    max_results (optional, default=5): The maximum number of results to return.\\n\\nReturns:\\n    The latest news from DuckDuckGo.&quot;,\n                    &quot;parameters&quot;: {\n                        &quot;type&quot;: &quot;object&quot;,\n                        &quot;properties&quot;: {\n                            &quot;query&quot;: {\n                                &quot;type&quot;: &quot;string&quot;\n                            },\n                            &quot;max_results&quot;: {\n                                &quot;type&quot;: [\n                                    &quot;number&quot;,\n                                    &quot;null&quot;\n                                ]\n                            }\n                        }\n                    }\n                }\n            }\n        ]\n    }\n    \n    response = requests.post(\n        proxy_url,\n        headers=header,\n        json=request\n    )\n    if response.status_code == 200:\n        res = response.json()\n        message = res[&#39;choices&#39;][0][&#39;message&#39;]\n        tools_response_messages = []\n        if not message[&#39;content&#39;] and &#39;tool_calls&#39; in message:\n            for tool_call in message[&#39;tool_calls&#39;]:\n                tool_name = tool_call[&#39;function&#39;][&#39;name&#39;]\n                tool_args = tool_call[&#39;function&#39;][&#39;arguments&#39;]\n                tool_args = json.loads(tool_args)\n                if tool_name not in function_map:\n                    print(f&quot;Error: {tool_name} is not a valid function name.&quot;)\n                    continue\n                tool_func = function_map[tool_name]\n                tool_response = tool_func(**tool_args)\n                tools_response_messages.append({\n                    &quot;role&quot;: &quot;tool&quot;, &quot;content&quot;: json.dumps(tool_response)\n                })\n    \n            if tools_response_messages:\n                request[&#39;messages&#39;] += tools_response_messages\n                response = requests.post(\n                    proxy_url,\n                    headers=header,\n                    json=request\n                )\n                if response.status_code == 200:\n                    res = response.json()\n                    print(res[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;])\n                else:\n                    print(&quot;Error:&quot;, response.status_code, response.text)\n        else:\n            print(message[&#39;content&#39;])\n    else:\n        print(&quot;Error:&quot;, response.status_code, response.text)\n    </code></pre>\n                    <h2 class=\"text-3xl font-bold mb-4\">Schema-less Function Call 🤩</h2>\n                    <p class=\"text-lg mb-8\">\n                        Check out the example file\n                        <a href=\"examples/example_3.py\" class=\"text-blue-500 underline\">example_3.py</a>\n                        for a full implementation of the following code.\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        In this method, we only need to provide the function's name, which consists of two parts, acting\n                        as a sort of namespace. The first part identifies the library or toolkit containing the\n                        functions, and the second part specifies the function's name, assuming it's already available on\n                        the proxy server. I aim to collaborate with the community to incorporate all typical functions,\n                        eliminating the need for passing a schema. Without having to handle function calls ourselves, a\n                        single request to the proxy enables it to identify and execute the functions, retrieve responses\n                        from large language models, and return the results to us. Thanks to Groq, all of this occurs in\n                        just seconds.\n                    </p>\n                    <pre\n                        class=\"bg-gray-800 rounded p-4 mb-4\"\n                    ><code class=\"language-python\">from duckduckgo_search import DDGS\n    import requests, os\n    api_key = userdata.get(&quot;GROQ_API_KEY&quot;)\n    header = {\n        &quot;Authorization&quot;: f&quot;Bearer {api_key}&quot;,\n        &quot;Content-Type&quot;: &quot;application/json&quot;\n    }\n    \n    proxy_url = &quot;https://groqcall.ai/proxy/groq/v1/chat/completions&quot;\n    \n    \n    request = {\n        &quot;messages&quot;: [\n            {\n                &quot;role&quot;: &quot;system&quot;,\n                &quot;content&quot;: &quot;YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\\n&lt;instructions&gt;\\n1. Use markdown to format your answers.\\n&lt;/instructions&gt;&quot;,\n            },\n            {\n                &quot;role&quot;: &quot;user&quot;,\n                &quot;content&quot;: &quot;Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.&quot;,\n            },\n        ],\n        &quot;model&quot;: &quot;mixtral-8x7b-32768&quot;,\n        &quot;tool_choice&quot;: &quot;auto&quot;,\n        &quot;tools&quot;: [\n            {\n                &quot;type&quot;: &quot;function&quot;,\n                &quot;function&quot;: {\n                    &quot;name&quot;: &quot;duckduck.search&quot;,\n                },\n            },\n            {\n                &quot;type&quot;: &quot;function&quot;,\n                &quot;function&quot;: {\n                    &quot;name&quot;: &quot;duckduck.news&quot;,\n                },\n            },\n        ],\n    }\n    \n    response = requests.post(\n        proxy_url,\n        headers=header,\n        json=request,\n    )\n    \n    if response.status_code == 200:\n        res = response.json()\n        print(res[&quot;choices&quot;][0][&quot;message&quot;][&quot;content&quot;])\n    else:\n        print(&quot;Error:&quot;, response.status_code, response.text)\n    </code></pre>\n                    <h2 class=\"text-3xl font-bold mb-4\">Using with PhiData</h2>\n                    <p class=\"text-lg mb-8\">\n                        Check out the example file\n                        <a href=\"examples/example_1.py\" class=\"text-blue-500 underline\">example_1.py</a>\n                        for a full implementation of the following code.\n                    </p>\n                    <p class=\"text-lg mb-8\">\n                        PhiData is a favorite of mine for creating AI assistants, thanks to its beautifully simplified\n                        interface, unlike the complexity seen in the LangChain library and LlamaIndex. I use it for many\n                        projects and want to give kudos to their team. It's open source, and I recommend everyone check\n                        it out. You can explore more from this link:\n                        <a href=\"https://github.com/phidatahq/phidata\" class=\"text-blue-500 underline\"\n                            >https://github.com/phidatahq/phidata</a\n                        >.\n                    </p>\n                    <pre\n                        class=\"bg-gray-800 rounded p-4 mb-4\"\n                    ><code class=\"language-python\">from google.README import userdata\n    from phi.llm.openai.like import OpenAILike\n    from phi.assistant import Assistant\n    from phi.tools.duckduckgo import DuckDuckGo\n    import os, json\n    \n    \n    my_groq = OpenAILike(\n            model=&quot;mixtral-8x7b-32768&quot;,\n            api_key=userdata.get(&quot;GROQ_API_KEY&quot;),\n            base_url=&quot;https://groqcall.ai/proxy/groq/v1&quot;\n        )\n    assistant = Assistant(\n        llm=my_groq,\n        tools=[DuckDuckGo()], show_tool_calls=True, markdown=True\n    )\n    assistant.print_response(&quot;Whats happening in France? Summarize top stories with sources, very short and concise.&quot;, stream=False)\n    \n    </code></pre>\n                    <h2 class=\"text-3xl font-bold mb-4\">Contributions Welcome! 🙌</h2>\n                    <p class=\"text-lg mb-8\">\n                        I am excited to extend and grow this repository by adding more built-in functions and\n                        integrating additional services. If you are interested in contributing to this project and being\n                        a part of its development, I would love to collaborate with you! I plan to create a discord\n                        channel for this project, where we can discuss ideas, share knowledge, and work together to\n                        enhance the repository.\n                    </p>\n                    <p class=\"text-lg mb-4\">Here's how you can get involved:</p>\n                    <ol class=\"list-decimal list-inside mb-8\">\n                        <li>Fork the repository and create your own branch.</li>\n                        <li>\n                            Implement new functions, integrate additional services, or make improvements to the existing\n                            codebase.\n                        </li>\n                        <li>Test your changes to ensure they work as expected.</li>\n                        <li>Submit a pull request describing the changes you have made and why they are valuable.</li>\n                    </ol>\n                    <p class=\"text-lg mb-8\">\n                        If you have any ideas, suggestions, or would like to discuss potential contributions, feel free\n                        to reach out to me. You can contact me through the following channels:\n                    </p>\n                    <ul class=\"list-disc list-inside mb-8\">\n                        <li>Twitter (X): @unclecode</li>\n                        <li>\n                            Email:\n                            <a href=\"mailto:unclecode@kidocode.com\" class=\"text-blue-500 underline\"\n                                >unclecode@kidocode.com</a\n                            >\n                        </li>\n                    </ul>\n                    <p class=\"text-lg mb-8\">\n                        I'm open to collaboration and excited to see how we can work together to enhance this project\n                        and provide value to the community. Let's connect and explore how we can help each other!\n                    </p>\n                    <p class=\"text-lg mb-8\">Together, let's make this repository even more awesome! 🚀</p>\n                </div>\n            </div>\n        </main>\n    </body>\n</html>\n"
  },
  {
    "path": "requirements.txt",
    "content": "aiohttp==3.9.3\naiosignal==1.3.1\nannotated-types==0.6.0\nanyio==4.3.0\nappnope==0.1.4\nasttokens==2.4.1\nasync-timeout==4.0.3\nattrs==23.2.0\nboto3==1.34.55\nbotocore==1.34.55\ncertifi==2024.2.2\ncffi==1.16.0\ncharset-normalizer==3.3.2\nclick==8.1.7\ncomm==0.2.1\ncurl_cffi==0.6.2\ndebugpy==1.8.1\ndecorator==5.1.1\ndistro==1.9.0\ndocker==7.0.0\nduckduckgo_search==4.5.0\nexceptiongroup==1.2.0\nexecuting==2.0.1\nfastapi==0.110.0\nfilelock==3.13.1\nfrozenlist==1.4.1\nfsspec==2024.2.0\ngitdb==4.0.11\nGitPython==3.1.42\ngroq==0.4.2\nh11==0.14.0\nhttpcore==1.0.4\nhttpx==0.25.2\nhuggingface-hub==0.21.3\nidna==3.6\nimportlib-metadata==7.0.1\nipykernel==6.29.3\nipython==8.22.2\njedi==0.19.1\nJinja2==3.1.3\njmespath==1.0.1\njupyter_client==8.6.0\njupyter_core==5.7.1\nlitellm==1.31.8\nlxml==5.1.0\nmarkdown-it-py==3.0.0\nMarkupSafe==2.1.5\nmatplotlib-inline==0.1.6\nmdurl==0.1.2\nmistralai==0.1.3\nmultidict==6.0.5\nnest-asyncio==1.6.0\nnumpy==1.26.4\nollama==0.1.7\nopenai==1.13.3\norjson==3.9.15\npackaging==23.2\npandas==2.2.1\nparso==0.8.3\npexpect==4.9.0\nphidata==2.3.50\nplatformdirs==4.2.0\nprompt-toolkit==3.0.43\npsutil==5.9.8\nptyprocess==0.7.0\npure-eval==0.2.2\npyarrow==15.0.0\npycparser==2.21\npydantic==2.6.3\npydantic-settings==2.2.1\npydantic_core==2.16.3\nPygments==2.17.2\npython-dateutil==2.9.0.post0\npython-dotenv==1.0.1\npytz==2024.1\nPyYAML==6.0.1\npyzmq==25.1.2\nregex==2023.12.25\nreplicate==0.24.0\nrequests==2.31.0\nrich==13.7.1\ns3transfer==0.10.0\nsix==1.16.0\nsmmap==5.0.1\nsniffio==1.3.1\nstack-data==0.6.3\nstarlette==0.36.3\ntiktoken==0.6.0\ntokenizers==0.15.2\ntomli==2.0.1\ntornado==6.4\ntqdm==4.66.2\ntraitlets==5.14.1\ntyper==0.9.0\ntyping_extensions==4.10.0\ntzdata==2024.1\nurllib3==2.0.7\nuvicorn==0.27.1\nwcwidth==0.2.13\nyarl==1.9.4\nzipp==3.17.0\n"
  }
]