Repository: unclecode/groqcall Branch: main Commit: 009c301a8a71 Files: 50 Total size: 414.1 KB Directory structure: gitextract_bsj671yq/ ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_old.md ├── app/ │ ├── __init__.py │ ├── config.py │ ├── functions/ │ │ ├── __init__.py │ │ ├── base.py │ │ └── duckduck.py │ ├── libs/ │ │ ├── __init__.py │ │ ├── base_handler.py │ │ ├── chains copy.py │ │ ├── chains.py │ │ ├── context.py │ │ ├── provider_handler.py │ │ ├── tools_handler.py │ │ └── vision_handler.py │ ├── main.py │ ├── models.py │ ├── prompts.py │ ├── providers.py │ ├── reasoning/ │ │ ├── __init__.py │ │ ├── base.py │ │ └── rerank.py │ ├── routes/ │ │ ├── __init__.py │ │ ├── examples.py │ │ └── proxy.py │ └── utils.py ├── cookbook/ │ ├── ai_assistant_custome_tools.py │ ├── cinemax.json │ ├── function_call_force_schema.py │ ├── function_call_force_tool_choice.py │ ├── function_call_ollama.py │ ├── function_call_phidata.py │ ├── function_call_vision.py │ ├── function_call_with_schema.py │ ├── function_call_without_schema.py │ ├── functiona_call_groq_langchain.py │ └── resources.py ├── examples/ │ ├── example_1.py │ ├── example_2.py │ ├── example_3.py │ └── example_4.py ├── frontend/ │ ├── assets/ │ │ ├── README.md │ │ ├── markdown.css │ │ └── style.css │ └── pages/ │ ├── index.html │ └── index_old.html └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/main.yml ================================================ name: Auto PR and Merge on Push by Specific User on: push: branches: - main jobs: auto-pr-and-merge: runs-on: ubuntu-latest steps: - name: Check User id: check_user run: | echo "user_matched=${{ github.actor == 'unclecode' }}" echo "user_matched=${{ github.actor == 'unclecode' }}" >> $GITHUB_ENV - name: Create Pull Request if: env.user_matched == 'true' id: create_pull_request uses: actions/github-script@v5 with: script: | const payload = { owner: context.repo.owner, repo: context.repo.repo, head: 'main', base: 'live', title: 'Auto PR from main to live', body: 'Automatically generated PR to keep live branch up-to-date', draft: false, }; // Create the pull request await github.rest.pulls.create(payload).then(pr => { core.setOutput('pr_number', pr.data.number); }).catch(err => core.setFailed(`Failed to create PR: ${err.message}`)); - name: Merge Pull Request if: env.user_matched == 'true' uses: actions/github-script@v5 with: script: | const pr_number = ${{ steps.create_pull_request.outputs.pr_number }}; if (!pr_number) { core.setFailed('PR number is undefined, skipping merge.'); return; } const payload = { owner: context.repo.owner, repo: context.repo.repo, pull_number: parseInt(pr_number, 10), merge_method: 'merge', // Options: 'merge', 'squash', or 'rebase' }; // Attempt to merge the pull request await github.rest.pulls.merge(payload).then(response => { if (response.status !== 200) { core.setFailed('Failed to merge the pull request'); } }).catch(err => core.setFailed(`Failed to merge PR: ${err.message}`)); ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ app.log .vscode app/routes/proxy_all_in_one.py ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "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. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "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. "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). "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. "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." "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. 2. 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. 3. 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. 4. 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: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You 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 If 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. You 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. 5. 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. 6. 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. 7. 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. 8. 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. 9. 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. END OF TERMS AND CONDITIONS ================================================ FILE: README.md ================================================ # GroqCall.ai - Lightning-Fast LLM Function Calls [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing) [![Version](https://img.shields.io/badge/version-0.0.5-blue.svg)](https://github.com/unclecode/groqcall) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) GroqCall 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. ## Quickstart ### Using the Pre-built Server To quickly start using GroqCall without running it locally, make requests to one of the following base URLs: - Cloud: `https://groqcall.ai/proxy/groq/v1` - Local: `http://localhost:8000` (if running the proxy server locally) ### Running the Proxy Locally 1. Clone the repository: ``` git clone https://github.com/unclecode/groqcall.git cd groqcall ``` 2. Create and activate a virtual environment: ``` python -m venv venv source venv/bin/activate ``` 3. Install dependencies: ``` pip install -r requirements.txt ``` 4. Run the FastAPI server: ``` ./venv/bin/uvicorn --app-dir app/ main:app --reload ``` ## Examples ### Using GroqCall with PhiData ```python from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo my_groq = OpenAILike( model="mixtral-8x7b-32768", api_key="YOUR_GROQ_API_KEY", base_url="https://groqcall.ai/proxy/groq/v1" # or "http://localhost:8000/proxy/groq/v1" if running locally ) assistant = Assistant( llm=my_groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("What's happening in France? Summarize top stories with sources, very short and concise.", stream=False) ``` ### Using GroqCall with Requests #### FuncHub: Schema-less Function Calls GroqCall introduces FuncHub, which allows you to make function calls without passing the function schema. ```python import requests api_key = "YOUR_GROQ_API_KEY" header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "What's happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduck.search" } }, { "type": "function", "function": { "name": "duckduck.news" } } ] } response = requests.post( proxy_url, headers=header, json=request ) print(response.json()["choices"][0]["message"]["content"]) ``` - 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. #### Passing Function Schemas If 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. #### Rune proxy with Ollama locally Function 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. ## Cookbook Explore the [Cookbook](https://github.com/unclecode/groqcall/tree/main/cookbook) for more examples and use cases of GroqCall. ## Motivation Groq 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. As 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. ## Contributing Contributions 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. Let's collaborate and make this repository even more awesome! 🚀 ## License This project is licensed under the Apache License 2.0. See [LICENSE](https://github.com/unclecode/groqcall/blob/main/LICENSE) for more information. ================================================ FILE: README_old.md ================================================ # GroqCall.ai (I changed the name from FunckyCall to GroqCall) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing) [![Version](https://img.shields.io/badge/version-0.0.1-blue.svg)](https://github.com/unclecode/groqcall) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) GroqCall 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. ## Motivation 🚀 Groq 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; I 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. Unable 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. Powered by Groq ## Running the Proxy Locally 🖥️ To run this proxy locally on your own machine, follow these steps: 1. Clone the GitHub repository: ```git clone https://github.com/unclecode/groqcall.git``` 2. Navigate to the project directory: ```cd groqcall``` 3. Create a virtual environment: ```python -m venv venv``` 4. Activate virtual environment: ```source venv/bin/activate``` 5. Install the required libraries: ```pip install -r requirements.txt``` 6. Run the FastAPI server: ```./venv/bin/uvicorn --app-dir app/ main:app --reload``` ## Using the Pre-built Server 🌐 For 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. To use the pre-built server, simply make requests to the following base URL: ```https://groqcall.ai/proxy/groq/v1``` ## Exploring GroqCall.ai 🚀 This README is organized into three main sections, each showcasing different aspects of GroqCall.ai: - **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. - **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. - **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. ```python # The following libraries are optional if you're interested in using PhiData or managing your tools on the client side. !pip install phidata > /dev/null !pip install openai > /dev/null !pip install duckduckgo-search > /dev/null ``` ## Sending POST request, with full functions implementation ```python from duckduckgo_search import DDGS import requests, os import json # Here you pass your own GROQ API key api_key=userdata.get("GROQ_API_KEY") header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] def duckduckgo_news(query, max_results=None): """ Use this function to get the latest news from DuckDuckGo. """ with DDGS() as ddgs: return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, "duckduckgo_news": duckduckgo_news, } request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } }, { "type": "function", "function": { "name": "duckduckgo_news", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } } ] } response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() message = res['choices'][0]['message'] tools_response_messages = [] if not message['content'] and 'tool_calls' in message: for tool_call in message['tool_calls']: tool_name = tool_call['function']['name'] tool_args = tool_call['function']['arguments'] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append({ "role": "tool", "content": json.dumps(tool_response) }) if tools_response_messages: request['messages'] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() print(res['choices'][0]['message']['content']) else: print("Error:", response.status_code, response.text) else: print(message['content']) else: print("Error:", response.status_code, response.text) ``` ## Schema-less Function Call 🤩 In 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. ```python from duckduckgo_search import DDGS import requests, os api_key = userdata.get("GROQ_API_KEY") header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n", }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.", }, ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduck.search", }, }, { "type": "function", "function": { "name": "duckduck.news", }, }, ], } response = requests.post( proxy_url, headers=header, json=request, ) if response.status_code == 200: res = response.json() print(res["choices"][0]["message"]["content"]) else: print("Error:", response.status_code, response.text) ``` ## Using with PhiData FindData 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. ```python from google.README import userdata from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo import os, json my_groq = OpenAILike( model="mixtral-8x7b-32768", api_key=userdata.get("GROQ_API_KEY"), base_url="https://groqcall.ai/proxy/groq/v1" ) assistant = Assistant( llm=my_groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) ``` ## Contributions Welcome! 🙌 I 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. Here's how you can get involved: 1. Fork the repository and create your own branch. 2. Implement new functions, integrate additional services, or make improvements to the existing codebase. 3. Test your changes to ensure they work as expected. 4. Submit a pull request describing the changes you have made and why they are valuable. If 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: - Twitter (X): @unclecode - Email: unclecode@kidocode.com ### Copyright 2024 Unclecode (Hossein Tohidi) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. I'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! Together, let's make this repository even more awesome! 🚀 ================================================ FILE: app/__init__.py ================================================ ================================================ FILE: app/config.py ================================================ # To be developed EVALUATION_CYCLES_COUNT=1 PARSE_ERROR_TRIES = 5 ================================================ FILE: app/functions/__init__.py ================================================ ================================================ FILE: app/functions/base.py ================================================ from pydantic import BaseModel from typing import Dict class Function: name: str description: str class Schema(BaseModel): pass @classmethod def get_schema(cls) -> Dict: schema_dict = { "name": cls.name, "description": cls.description, "parameters": cls.Schema.schema(), } return schema_dict ================================================ FILE: app/functions/duckduck.py ================================================ from pydantic import BaseModel, Field from typing import Optional, Dict import requests import json from duckduckgo_search import DDGS # from .base import Function from pydantic import Field from typing import Optional import requests import json from .base import Function class SearchFunction(Function): name = "duckduck.search" 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." class Schema(Function.Schema): query: str = Field(..., description="The query to search for.") max_results: Optional[int] = Field(5, description="The maximum number of results to return.") @classmethod def run(cls, **kwargs): query = kwargs.get("query") max_results = kwargs.get("max_results") with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] class NewsFunction(Function): name = "duckduck.news" 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." class Schema(Function.Schema): query: str = Field(..., description="The query to search for.") max_results: Optional[int] = Field(5, description="The maximum number of results to return.") @classmethod def run(cls, **kwargs): query = kwargs.get("query") max_results = kwargs.get("max_results") with DDGS() as ddgs: results = [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] return results ================================================ FILE: app/libs/__init__.py ================================================ from .context import Context from .base_handler import Handler, DefaultCompletionHandler, ExceptionHandler, FallbackHandler from .provider_handler import ProviderSelectionHandler from .vision_handler import ImageMessageHandler from .tools_handler import ToolExtractionHandler, ToolResponseHandler __all__ = [ "Context", "Handler", "DefaultCompletionHandler", "ExceptionHandler", "ProviderSelectionHandler", "ImageMessageHandler", "ToolExtractionHandler", "ToolResponseHandler", "FallbackHandler", ] ================================================ FILE: app/libs/base_handler.py ================================================ from abc import ABC, abstractmethod from .context import Context from fastapi.responses import JSONResponse import traceback class Handler(ABC): """Abstract Handler class for building the chain of handlers.""" _next_handler: "Handler" = None def set_next(self, handler: "Handler") -> "Handler": self._next_handler = handler return handler @abstractmethod async def handle(self, context: Context): if self._next_handler: try: return await self._next_handler.handle(context) except Exception as e: _exception_handler: "Handler" = ExceptionHandler() # Extract the stack trace and log the exception return await _exception_handler.handle(self._next_handler, context, e) class DefaultCompletionHandler(Handler): async def handle(self, context: Context): if context.is_normal_chat: # Assuming context.client is set and has a method for creating chat completions completion = context.client.route( messages=context.messages, **context.client.clean_params(context.params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) return await super().handle(context) class FallbackHandler(Handler): async def handle(self, context: Context): # This handler does not pass the request further down the chain. # It acts as a fallback when no other handler has processed the request. if not context.response: # The default action when no other handlers have processed the request context.response = {"message": "No suitable action found for the request."} return JSONResponse(content=context.response, status_code=400) # If there's already a response set in the context, it means one of the handlers has processed the request. return JSONResponse(content=context.response, status_code=200) class ExceptionHandler(Handler): async def handle(self, handler: Handler, context: Context, exception: Exception): print(f"Error processing the request: {str(handler.__class__) } - {exception}") # print(traceback.format_exc()) return JSONResponse( content={"error": "An unexpected error occurred, within handler " + str(handler.__class__) + " : " + str(exception)}, status_code=500, ) ================================================ FILE: app/libs/chains copy.py ================================================ from abc import ABC, abstractmethod from typing import Any, Dict from importlib import import_module import json import uuid import traceback from fastapi import Request from fastapi.responses import JSONResponse from providers import BaseProvider from prompts import * from providers import GroqProvider import importlib from utils import get_tool_call_response, create_logger, describe missed_tool_logger = create_logger( "chain.missed_tools", ".logs/empty_tool_tool_response.log" ) class Context: def __init__(self, request: Request, provider: str, body: Dict[str, Any]): self.request = request self.provider = provider self.body = body self.response = None # extract all keys from body except messages and tools and set in params self.params = {k: v for k, v in body.items() if k not in ["messages", "tools"]} # self.no_tool_behaviour = self.params.get("no_tool_behaviour", "return") self.no_tool_behaviour = self.params.get("no_tool_behaviour", "forward") self.params.pop("no_tool_behaviour", None) # Todo: For now, no stream, sorry ;) self.params["stream"] = False self.messages = body.get("messages", []) self.tools = body.get("tools", []) self.builtin_tools = [ t for t in self.tools if "parameters" not in t["function"] ] self.builtin_tool_names = [t["function"]["name"] for t in self.builtin_tools] self.custom_tools = [t for t in self.tools if "parameters" in t["function"]] for bt in self.builtin_tools: func_namespace = bt["function"]["name"] if len(func_namespace.split(".")) == 2: module_name, func_class_name = func_namespace.split(".") func_class_name = f"{func_class_name.capitalize()}Function" # raise ValueError("Only one builtin function can be called at a time.") module = importlib.import_module(f"app.functions.{module_name}") func_class = getattr(module, func_class_name, None) schema_dict = func_class.get_schema() if schema_dict: bt["function"] = schema_dict bt["run"] = func_class.run bt["extra"] = self.params.get("extra", {}) self.params.pop("extra", None) self.client: BaseProvider = None @property def last_message(self): return self.messages[-1] if self.messages else {} @property def is_tool_call(self): return bool( self.last_message["role"] == "user" and self.tools and self.params.get("tool_choice", "none") != "none" ) @property def is_tool_response(self): return bool(self.last_message["role"] == "tool" and self.tools) @property def is_normal_chat(self): return bool(not self.is_tool_call and not self.is_tool_response) class Handler(ABC): """Abstract Handler class for building the chain of handlers.""" _next_handler: "Handler" = None def set_next(self, handler: "Handler") -> "Handler": self._next_handler = handler return handler @abstractmethod async def handle(self, context: Context): if self._next_handler: try: return await self._next_handler.handle(context) except Exception as e: _exception_handler: "Handler" = ExceptionHandler() # Extract the stack trace and log the exception return await _exception_handler.handle(context, e) class ProviderSelectionHandler(Handler): @staticmethod def provider_exists(provider: str) -> bool: module_name = f"app.providers" class_name = f"{provider.capitalize()}Provider" try: provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) return bool(provider_class) except ImportError: return False async def handle(self, context: Context): # Construct the module path and class name based on the provider module_name = f"app.providers" class_name = f"{context.provider.capitalize()}Provider" try: # Dynamically import the module and class provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) if provider_class: context.client = provider_class( api_key=context.api_token ) # Assuming an api_key parameter return await super().handle(context) else: raise ValueError( f"Provider class {class_name} could not be found in {module_name}." ) except ImportError as e: # Handle import error (e.g., module or class not found) print(f"Error importing {class_name} from {module_name}: {e}") context.response = { "error": f"An error occurred while trying to load the provider: {e}" } return JSONResponse(content=context.response, status_code=500) class ImageMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: if message["role"] == "user": if isinstance(message["content"], list): prompt = None for content in message["content"]: if content["type"] == "text": # new_messages.append({"role": message["role"], "content": content["text"]}) prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: prompt = prompt or IMAGE_DESCRIPTO_PROMPT description = describe(prompt, image_url) if description: description = get_image_desc_guide(image_ref, description) new_messages.append( {"role": message["role"], "content": description} ) image_ref += 1 else: pass except Exception as e: print(f"Error describing image: {e}") continue else: new_messages.append(message) else: new_messages.append(message) context.messages = new_messages return await super().handle(context) class ImageLLavaMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: new_messages.append(message) if message["role"] == "user": if isinstance(message["content"], list): for content in message["content"]: if content["type"] == "text": prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: description = describe(prompt, image_url) new_messages.append( {"role": "assistant", "content": description} ) image_ref += 1 except Exception as e: print(f"Error describing image: {e}") continue context.messages = new_messages return await super().handle(context) class ToolExtractionHandler(Handler): async def handle(self, context: Context): body = context.body if context.is_tool_call: # Prepare the messages and tools for the tool extraction messages = [ f"{m['role'].title()}: {m['content']}" for m in context.messages if m["role"] != "system" ] tools_json = json.dumps([t["function"] for t in context.tools], indent=4) # Process the tool_choice tool_choice = context.params.get("tool_choice", "auto") forced_mode = False if ( type(tool_choice) == dict and tool_choice.get("type", None) == "function" ): tool_choice = tool_choice["function"].get("name", None) if not tool_choice: raise ValueError( "Invalid tool choice. 'tool_choice' is set to a dictionary with 'type' as 'function', but 'function' does not have a 'name' key." ) forced_mode = True # Regenerate the string tool_json and keep only the forced tool tools_json = json.dumps( [ t["function"] for t in context.tools if t["function"]["name"] == tool_choice ], indent=4, ) system_message = ( SYSTEM_MESSAGE if not forced_mode else ENFORCED_SYSTAME_MESSAE ) suffix = SUFFIX if not forced_mode else get_forced_tool_suffix(tool_choice) new_messages = [ {"role": "system", "content": system_message}, { "role": "system", "content": f"Conversation History:\n{''.join(messages)}\n\nTools: \n{tools_json}\n\n{suffix}", }, ] completion, tool_calls = await self.process_tool_calls( context, new_messages ) if not tool_calls: if context.no_tool_behaviour == "forward": context.tools = None return await super().handle(context) else: context.response = {"tool_calls": []} tool_response = get_tool_call_response(completion, [], []) missed_tool_logger.debug( f"Last message content: {context.last_message['content']}" ) return JSONResponse(content=tool_response, status_code=200) unresolved_tol_calls = [ t for t in tool_calls if t["function"]["name"] not in context.builtin_tool_names ] resolved_responses = [] for tool in tool_calls: for bt in context.builtin_tools: if tool["function"]["name"] == bt["function"]["name"]: res = bt["run"]( **{ **json.loads(tool["function"]["arguments"]), **bt["extra"], } ) resolved_responses.append( { "name": tool["function"]["name"], "role": "tool", "content": json.dumps(res), "tool_call_id": "chatcmpl-" + completion.id, } ) if not unresolved_tol_calls: context.messages.extend(resolved_responses) return await super().handle(context) tool_response = get_tool_call_response( completion, unresolved_tol_calls, resolved_responses ) context.response = tool_response return JSONResponse(content=context.response, status_code=200) return await super().handle(context) async def process_tool_calls(self, context, new_messages): try: tries = 5 tool_calls = [] while tries > 0: try: # Assuming the context has an instantiated client according to the selected provider completion = context.client.route( model=context.client.parser_model, messages=new_messages, temperature=0, max_tokens=1024, top_p=1, stream=False, ) response = completion.choices[0].message.content if "```json" in response: response = response.split("```json")[1].split("```")[0] try: tool_response = json.loads(response) if isinstance(tool_response, list): tool_response = {"tool_calls": tool_response} except json.JSONDecodeError as e: print( f"Error parsing the tool response: {e}, tries left: {tries}" ) new_messages.append( { "role": "user", "content": f"Error: {e}.\n\n{CLEAN_UP_MESSAGE}", } ) tries -= 1 continue for func in tool_response.get("tool_calls", []): tool_calls.append( { "id": f"call_{func['name']}_{str(uuid.uuid4())}", "type": "function", "function": { "name": func["name"], "arguments": json.dumps(func["arguments"]), }, } ) break except Exception as e: raise e if tries == 0: tool_calls = [] return completion, tool_calls except Exception as e: print(f"Error processing the tool calls: {e}") raise e class ToolResponseHandler(Handler): async def handle(self, context: Context): body = context.body if context.is_tool_response: messages = context.messages for message in messages: if message["role"] == "tool": message["role"] = "user" message["content"] = get_func_result_guide(message["content"]) messages[-1]["role"] = "user" # Assuming get_func_result_guide is a function that formats the tool response messages[-1]["content"] = get_func_result_guide(messages[-1]["content"]) try: completion = context.client.route( messages=messages, **context.client.clean_params(context.params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) except Exception as e: # Log the exception or handle it as needed print(e) context.response = { "error": "An error occurred processing the tool response" } return JSONResponse(content=context.response, status_code=500) return await super().handle(context) class DefaultCompletionHandler(Handler): async def handle(self, context: Context): if context.is_normal_chat: # Assuming context.client is set and has a method for creating chat completions completion = context.client.route( messages=context.messages, **context.client.clean_params(context.params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) return await super().handle(context) class FallbackHandler(Handler): async def handle(self, context: Context): # This handler does not pass the request further down the chain. # It acts as a fallback when no other handler has processed the request. if not context.response: # The default action when no other handlers have processed the request context.response = {"message": "No suitable action found for the request."} return JSONResponse(content=context.response, status_code=400) # If there's already a response set in the context, it means one of the handlers has processed the request. return JSONResponse(content=context.response, status_code=200) class ExceptionHandler(Handler): async def handle(self, context: Context, exception: Exception): print(f"Error processing the request: {exception}") print(traceback.format_exc()) return JSONResponse( content={"error": "An unexpected error occurred. " + str(exception)}, status_code=500, ) ================================================ FILE: app/libs/chains.py ================================================ from abc import ABC, abstractmethod from typing import Any, Dict from importlib import import_module import json import uuid import traceback from fastapi import Request from fastapi.responses import JSONResponse from providers import BaseProvider from prompts import * from providers import GroqProvider import importlib from utils import get_tool_call_response, create_logger, describe missed_tool_logger = create_logger( "chain.missed_tools", ".logs/empty_tool_tool_response.log" ) class Context: def __init__(self, request: Request, provider: str, body: Dict[str, Any]): self.request = request self.provider = provider self.body = body self.response = None # extract all keys from body except messages and tools and set in params self.params = {k: v for k, v in body.items() if k not in ["messages", "tools"]} # self.no_tool_behaviour = self.params.get("no_tool_behaviour", "return") self.no_tool_behaviour = self.params.get("no_tool_behaviour", "forward") self.params.pop("no_tool_behaviour", None) # Todo: For now, no stream, sorry ;) self.params["stream"] = False self.messages = body.get("messages", []) self.tools = body.get("tools", []) self.builtin_tools = [ t for t in self.tools if "parameters" not in t["function"] ] self.builtin_tool_names = [t["function"]["name"] for t in self.builtin_tools] self.custom_tools = [t for t in self.tools if "parameters" in t["function"]] for bt in self.builtin_tools: func_namespace = bt["function"]["name"] if len(func_namespace.split(".")) == 2: module_name, func_class_name = func_namespace.split(".") func_class_name = f"{func_class_name.capitalize()}Function" # raise ValueError("Only one builtin function can be called at a time.") module = importlib.import_module(f"app.functions.{module_name}") func_class = getattr(module, func_class_name, None) schema_dict = func_class.get_schema() if schema_dict: bt["function"] = schema_dict bt["run"] = func_class.run bt["extra"] = self.params.get("extra", {}) self.params.pop("extra", None) self.client: BaseProvider = None @property def last_message(self): return self.messages[-1] if self.messages else {} @property def is_tool_call(self): return bool( self.last_message["role"] == "user" and self.tools and self.params.get("tool_choice", "none") != "none" ) @property def is_tool_response(self): return bool(self.last_message["role"] == "tool" and self.tools) @property def is_normal_chat(self): return bool(not self.is_tool_call and not self.is_tool_response) class Handler(ABC): """Abstract Handler class for building the chain of handlers.""" _next_handler: "Handler" = None def set_next(self, handler: "Handler") -> "Handler": self._next_handler = handler return handler @abstractmethod async def handle(self, context: Context): if self._next_handler: try: return await self._next_handler.handle(context) except Exception as e: _exception_handler: "Handler" = ExceptionHandler() # Extract the stack trace and log the exception return await _exception_handler.handle(context, e) class ProviderSelectionHandler(Handler): @staticmethod def provider_exists(provider: str) -> bool: module_name = f"app.providers" class_name = f"{provider.capitalize()}Provider" try: provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) return bool(provider_class) except ImportError: return False async def handle(self, context: Context): # Construct the module path and class name based on the provider module_name = f"app.providers" class_name = f"{context.provider.capitalize()}Provider" try: # Dynamically import the module and class provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) if provider_class: context.client = provider_class( api_key=context.api_token ) # Assuming an api_key parameter return await super().handle(context) else: raise ValueError( f"Provider class {class_name} could not be found in {module_name}." ) except ImportError as e: # Handle import error (e.g., module or class not found) print(f"Error importing {class_name} from {module_name}: {e}") context.response = { "error": f"An error occurred while trying to load the provider: {e}" } return JSONResponse(content=context.response, status_code=500) class ImageMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: if message["role"] == "user": if isinstance(message["content"], list): prompt = None for content in message["content"]: if content["type"] == "text": # new_messages.append({"role": message["role"], "content": content["text"]}) prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: prompt = prompt or IMAGE_DESCRIPTO_PROMPT description = describe(prompt, image_url) if description: description = get_image_desc_guide(image_ref, description) new_messages.append( {"role": message["role"], "content": description} ) image_ref += 1 else: pass except Exception as e: print(f"Error describing image: {e}") continue else: new_messages.append(message) else: new_messages.append(message) context.messages = new_messages return await super().handle(context) class ImageLLavaMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: new_messages.append(message) if message["role"] == "user": if isinstance(message["content"], list): for content in message["content"]: if content["type"] == "text": prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: description = describe(prompt, image_url) new_messages.append( {"role": "assistant", "content": description} ) image_ref += 1 except Exception as e: print(f"Error describing image: {e}") continue context.messages = new_messages return await super().handle(context) class ToolExtractionHandler(Handler): async def handle(self, context: Context): body = context.body if context.is_tool_call: # Prepare the messages and tools for the tool extraction messages = [ f"{m['role'].title()}: {m['content']}" for m in context.messages if m["role"] != "system" ] tools_json = json.dumps([t["function"] for t in context.tools], indent=4) # Process the tool_choice tool_choice = context.params.get("tool_choice", "auto") forced_mode = False if ( type(tool_choice) == dict and tool_choice.get("type", None) == "function" ): tool_choice = tool_choice["function"].get("name", None) if not tool_choice: raise ValueError( "Invalid tool choice. 'tool_choice' is set to a dictionary with 'type' as 'function', but 'function' does not have a 'name' key." ) forced_mode = True # Regenerate the string tool_json and keep only the forced tool tools_json = json.dumps( [ t["function"] for t in context.tools if t["function"]["name"] == tool_choice ], indent=4, ) system_message = ( SYSTEM_MESSAGE if not forced_mode else ENFORCED_SYSTAME_MESSAE ) suffix = SUFFIX if not forced_mode else get_forced_tool_suffix(tool_choice) new_messages = [ {"role": "system", "content": system_message}, { "role": "system", "content": f"Conversation History:\n{''.join(messages)}\n\nTools: \n{tools_json}\n\n{suffix}", }, ] completion, tool_calls = await self.process_tool_calls( context, new_messages ) if not tool_calls: if context.no_tool_behaviour == "forward": context.tools = None return await super().handle(context) else: context.response = {"tool_calls": []} tool_response = get_tool_call_response(completion, [], []) missed_tool_logger.debug( f"Last message content: {context.last_message['content']}" ) return JSONResponse(content=tool_response, status_code=200) unresolved_tol_calls = [ t for t in tool_calls if t["function"]["name"] not in context.builtin_tool_names ] resolved_responses = [] for tool in tool_calls: for bt in context.builtin_tools: if tool["function"]["name"] == bt["function"]["name"]: res = bt["run"]( **{ **json.loads(tool["function"]["arguments"]), **bt["extra"], } ) resolved_responses.append( { "name": tool["function"]["name"], "role": "tool", "content": json.dumps(res), "tool_call_id": "chatcmpl-" + completion.id, } ) if not unresolved_tol_calls: context.messages.extend(resolved_responses) return await super().handle(context) tool_response = get_tool_call_response( completion, unresolved_tol_calls, resolved_responses ) context.response = tool_response return JSONResponse(content=context.response, status_code=200) return await super().handle(context) async def process_tool_calls(self, context, new_messages): try: tries = 5 tool_calls = [] while tries > 0: try: # Assuming the context has an instantiated client according to the selected provider completion = context.client.route( model=context.client.parser_model, messages=new_messages, temperature=0, max_tokens=1024, top_p=1, stream=False, ) response = completion.choices[0].message.content if "```json" in response: response = response.split("```json")[1].split("```")[0] try: tool_response = json.loads(response) if isinstance(tool_response, list): tool_response = {"tool_calls": tool_response} except json.JSONDecodeError as e: print( f"Error parsing the tool response: {e}, tries left: {tries}" ) new_messages.append( { "role": "user", "content": f"Error: {e}.\n\n{CLEAN_UP_MESSAGE}", } ) tries -= 1 continue for func in tool_response.get("tool_calls", []): tool_calls.append( { "id": f"call_{func['name']}_{str(uuid.uuid4())}", "type": "function", "function": { "name": func["name"], "arguments": json.dumps(func["arguments"]), }, } ) break except Exception as e: raise e if tries == 0: tool_calls = [] return completion, tool_calls except Exception as e: print(f"Error processing the tool calls: {e}") raise e class ToolResponseHandler(Handler): async def handle(self, context: Context): body = context.body if context.is_tool_response: messages = context.messages for message in messages: if message["role"] == "tool": message["role"] = "user" message["content"] = get_func_result_guide(message["content"]) messages[-1]["role"] = "user" # Assuming get_func_result_guide is a function that formats the tool response messages[-1]["content"] = get_func_result_guide(messages[-1]["content"]) try: completion = context.client.route( messages=messages, **context.client.clean_params(context.params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) except Exception as e: # Log the exception or handle it as needed print(e) context.response = { "error": "An error occurred processing the tool response" } return JSONResponse(content=context.response, status_code=500) return await super().handle(context) class DefaultCompletionHandler(Handler): async def handle(self, context: Context): if context.is_normal_chat: # Assuming context.client is set and has a method for creating chat completions completion = context.client.route( messages=context.messages, **context.client.clean_params(context.params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) return await super().handle(context) class FallbackHandler(Handler): async def handle(self, context: Context): # This handler does not pass the request further down the chain. # It acts as a fallback when no other handler has processed the request. if not context.response: # The default action when no other handlers have processed the request context.response = {"message": "No suitable action found for the request."} return JSONResponse(content=context.response, status_code=400) # If there's already a response set in the context, it means one of the handlers has processed the request. return JSONResponse(content=context.response, status_code=200) class ExceptionHandler(Handler): async def handle(self, context: Context, exception: Exception): print(f"Error processing the request: {exception}") print(traceback.format_exc()) return JSONResponse( content={"error": "An unexpected error occurred. " + str(exception)}, status_code=500, ) ================================================ FILE: app/libs/context.py ================================================ from typing import Any, Dict from fastapi import Request from providers import BaseProvider from prompts import * import importlib from utils import create_logger class Context: def __init__(self, request: Request, provider: str, body: Dict[str, Any]): self.request = request self.provider = provider self.body = body self.response = None # extract all keys from body except messages and tools and set in params self.params = {k: v for k, v in body.items() if k not in ["messages", "tools"]} # self.no_tool_behaviour = self.params.get("no_tool_behaviour", "return") self.no_tool_behaviour = self.params.get("no_tool_behaviour", "forward") self.params.pop("no_tool_behaviour", None) # Todo: For now, no stream, sorry ;) self.params["stream"] = False self.messages = body.get("messages", []) self.tools = body.get("tools", []) self.builtin_tools = [ t for t in self.tools if "parameters" not in t["function"] ] self.builtin_tool_names = [t["function"]["name"] for t in self.builtin_tools] self.custom_tools = [t for t in self.tools if "parameters" in t["function"]] for bt in self.builtin_tools: func_namespace = bt["function"]["name"] if len(func_namespace.split(".")) == 2: module_name, func_class_name = func_namespace.split(".") func_class_name = f"{func_class_name.capitalize()}Function" # raise ValueError("Only one builtin function can be called at a time.") module = importlib.import_module(f"app.functions.{module_name}") func_class = getattr(module, func_class_name, None) schema_dict = func_class.get_schema() if schema_dict: bt["function"] = schema_dict bt["run"] = func_class.run bt["extra"] = self.params.get("extra", {}) self.params.pop("extra", None) self.client: BaseProvider = None @property def last_message(self): return self.messages[-1] if self.messages else {} @property def is_tool_call(self): return bool( self.last_message["role"] == "user" and self.tools and self.params.get("tool_choice", None) != "none" ) @property def is_tool_response(self): return bool(self.last_message["role"] == "tool" and self.tools) @property def is_normal_chat(self): return bool(not self.is_tool_call and not self.is_tool_response) ================================================ FILE: app/libs/provider_handler.py ================================================ from importlib import import_module from fastapi.responses import JSONResponse from prompts import * from .base_handler import Handler from .context import Context class ProviderSelectionHandler(Handler): @staticmethod def provider_exists(provider: str) -> bool: module_name = f"app.providers" class_name = f"{provider.capitalize()}Provider" try: provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) return bool(provider_class) except ImportError: return False async def handle(self, context: Context): # Construct the module path and class name based on the provider module_name = f"app.providers" class_name = f"{context.provider.capitalize()}Provider" try: # Dynamically import the module and class provider_module = import_module(module_name) provider_class = getattr(provider_module, class_name) if provider_class: context.client = provider_class( api_key=context.api_token ) # Assuming an api_key parameter return await super().handle(context) else: raise ValueError( f"Provider class {class_name} could not be found in {module_name}." ) except ImportError as e: # Handle import error (e.g., module or class not found) print(f"Error importing {class_name} from {module_name}: {e}") context.response = { "error": f"An error occurred while trying to load the provider: {e}" } return JSONResponse(content=context.response, status_code=500) ================================================ FILE: app/libs/tools_handler.py ================================================ import concurrent.futures import uuid import json import math from fastapi.responses import JSONResponse from prompts import * from .base_handler import Handler, Context from .context import Context from utils import get_tool_call_response, create_logger, describe from config import PARSE_ERROR_TRIES, EVALUATION_CYCLES_COUNT from collections import defaultdict missed_tool_logger = create_logger( "chain.missed_tools", ".logs/empty_tool_tool_response.log" ) class ImageLLavaMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: new_messages.append(message) if message["role"] == "user": if isinstance(message["content"], list): for content in message["content"]: if content["type"] == "text": prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: description = describe(prompt, image_url) new_messages.append( {"role": "assistant", "content": description} ) image_ref += 1 except Exception as e: print(f"Error describing image: {e}") continue context.messages = new_messages return await super().handle(context) class ToolExtractionHandler(Handler): async def handle(self, context: Context): if not context.is_tool_call: return await super().handle(context) # Step 1: Prepare the conversation history messages = self._prepare_conversation_history(context.messages) # Step 2: Prepare tool details and detect the mode of operation available_tools, system_message, suffix = self._prepare_tool_details(context) # Step 3: Prepare the messages for the model new_messages = self._prepare_model_messages(messages, available_tools, suffix, context.messages[-1]['content'], system_message) # Step 4: Detect the tool calls tool_calls_result = await self.process_tool_calls(context, new_messages) tool_calls = tool_calls_result["tool_calls"] # Step 5: Handle the situation where no tool calls are detected if not tool_calls: return await self._handle_no_tool_calls(context, tool_calls_result) # Step 6: Process built-in tools and resolve the tool calls unresolved_tool_calls, resolved_responses = self._process_builtin_tools(context, tool_calls, tool_calls_result["last_completion"].id) if not unresolved_tool_calls: context.messages.extend(resolved_responses) return await super().handle(context) # Step 7: Return the unresolved tool calls to the client tool_response = get_tool_call_response(tool_calls_result, unresolved_tool_calls, resolved_responses) context.response = tool_response return JSONResponse(content=context.response, status_code=200) def _prepare_conversation_history(self, messages): return [ f"<{m['role'].lower()}>\n{m['content']}\n" for m in messages if m["role"] != "system" ] def _prepare_tool_details(self, context): tool_choice = context.params.get("tool_choice", "auto") forced_mode = type(tool_choice) == dict and tool_choice.get("type", None) == "function" available_tools = [] if forced_mode: tool_choice = tool_choice["function"]["name"] available_tools = [t["function"] for t in context.tools if t["function"]["name"] == tool_choice] system_message = ENFORCED_SYSTAME_MESSAE suffix = get_forced_tool_suffix(tool_choice) else: tool_choice = "auto" available_tools = [t["function"] for t in context.tools] system_message = SYSTEM_MESSAGE suffix = get_suffix() # 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. # available_tools.append({ # "name": "fallback", # "description": "Use this tool when none of the other tools are useful for the user input.", # "arguments": {}} # ) return available_tools, system_message, suffix def _prepare_model_messages(self, messages, available_tools, suffix, last_message_content, system_message): messages_flatten = "\n".join(messages) tools_json = json.dumps(available_tools, indent=4) return [ {"role": "system", "content": system_message}, { "role": "user", "content": f"# Conversation History:\n{messages_flatten}\n\n# Available Tools: \n{tools_json}\n\n{suffix}\n{last_message_content}", }, ] async def _handle_no_tool_calls(self, context, tool_calls_result): if context.no_tool_behaviour == "forward": context.tools = None return await super().handle(context) else: context.response = {"tool_calls": []} tool_response = get_tool_call_response(tool_calls_result, [], []) missed_tool_logger.debug(f"Last message content: {context.last_message['content']}") return JSONResponse(content=tool_response, status_code=200) def _process_builtin_tools(self, context, tool_calls, tool_calls_result_id): unresolved_tool_calls = [ t for t in tool_calls if t["function"]["name"] not in context.builtin_tool_names ] resolved_responses = [] for tool in tool_calls: for bt in context.builtin_tools: if tool["function"]["name"] == bt["function"]["name"]: res = bt["run"](**{**json.loads(tool["function"]["arguments"]), **bt["extra"]}) resolved_responses.append({ "name": tool["function"]["name"], "role": "tool", "content": json.dumps(res), "tool_call_id": "chatcmpl-" + tool_calls_result_id, }) return unresolved_tool_calls, resolved_responses async def handle1(self, context: Context): body = context.body if context.is_tool_call: # Step 1: Prepare the the history of conversation. messages = [ f"<{m['role'].lower()}>\n{m['content']}\n" for m in context.messages if m["role"] != "system" ] messages_flatten = "\n".join(messages) # Step 2: Prepare tools details and detect the mode of operation. tool_choice = context.params.get("tool_choice", "auto") forced_mode = type(tool_choice) == dict and tool_choice.get("type", None) == "function" if forced_mode: tool_choice = tool_choice["function"]["name"] tools_json = json.dumps([t["function"] for t in context.tools if t["function"]["name"] == tool_choice], indent=4) system_message = ENFORCED_SYSTAME_MESSAE suffix = get_forced_tool_suffix(tool_choice) else: tool_choice = "auto" tools_json = json.dumps([t["function"] for t in context.tools], indent=4) system_message = SYSTEM_MESSAGE suffix = SUFFIX # Step 3: Prepare the messages for the model. new_messages = [ {"role": "system", "content": system_message}, { "role": "user", "content": f"# Conversation History:\n{messages_flatten}\n\n# Available Tools: \n{tools_json}\n\n{suffix}\n{context.messages[-1]['content']}", }, ] # Step 4: Detect the tool calls. tool_calls_result = await self.process_tool_calls(context, new_messages) tool_calls = tool_calls_result["tool_calls"] # Step 5: Handle the situation where no tool calls are detected. if not tool_calls: if context.no_tool_behaviour == "forward": context.tools = None return await super().handle(context) else: context.response = {"tool_calls": []} tool_response = get_tool_call_response(tool_calls_result, [], []) missed_tool_logger.debug( f"Last message content: {context.last_message['content']}" ) return JSONResponse(content=tool_response, status_code=200) # 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. unresolved_tol_calls = [ t for t in tool_calls if t["function"]["name"] not in context.builtin_tool_names ] resolved_responses = [] for tool in tool_calls: for bt in context.builtin_tools: if tool["function"]["name"] == bt["function"]["name"]: res = bt["run"]( **{ **json.loads(tool["function"]["arguments"]), **bt["extra"], } ) resolved_responses.append( { "name": tool["function"]["name"], "role": "tool", "content": json.dumps(res), "tool_call_id": "chatcmpl-" + tool_calls_result.id, } ) if not unresolved_tol_calls: context.messages.extend(resolved_responses) return await super().handle(context) # Step 7: If reach here, it means there are unresolved tool calls. We will return the tool calls to the client to resolve them. tool_response = get_tool_call_response( tool_calls_result, unresolved_tol_calls, resolved_responses ) context.response = tool_response return JSONResponse(content=context.response, status_code=200) return await super().handle(context) async def process_tool_calls(self, context, new_messages): try: evaluation_cycles_count = EVALUATION_CYCLES_COUNT def call_route(messages): completion = context.client.route( model=context.client.parser_model, messages=messages, temperature=0, max_tokens=512, top_p=1, stream=False, ) response = completion.choices[0].message.content response = response.replace("\_", "_") if TOOLS_OPEN_TOKEN in response: response = response.split(TOOLS_OPEN_TOKEN)[1].split( TOOLS_CLOSE_TOKEN )[0] if "```json" in response: response = response.split("```json")[1].split("```")[0] try: tool_response = json.loads(response) if isinstance(tool_response, list): tool_response = {"tool_calls": tool_response} # Check all detected functions exist in the available tools valid_names = [t['function']["name"] for t in context.tools] available_tools = [t for t in tool_response.get("tool_calls", []) if t['name'] in valid_names] tool_response = { "tool_calls": available_tools, } # tool_response = {"tool_calls": []} return tool_response.get("tool_calls", []), completion except json.JSONDecodeError as e: print(f"Error parsing the tool response: {e}") return [], None with concurrent.futures.ThreadPoolExecutor() as executor: futures = [ executor.submit(call_route, new_messages) for _ in range(evaluation_cycles_count) ] results = [ future.result() for future in concurrent.futures.as_completed(futures) ] tool_calls_list, completions = zip(*results) tool_calls_count = defaultdict(int) for tool_calls in tool_calls_list: for func in tool_calls: tool_calls_count[func["name"]] += 1 pickup_threshold = math.floor(evaluation_cycles_count * 0.7) final_tool_calls = [] for tool_calls in tool_calls_list: for func in tool_calls: if tool_calls_count[func["name"]] >= pickup_threshold: # ppend if function is not already in the list if not any( f['function']["name"] == func["name"] for f in final_tool_calls ): final_tool_calls.append( { "id": f"call_{func['name']}_{str(uuid.uuid4())}", "type": "function", "function": { "name": func["name"], "arguments": json.dumps(func["arguments"]), }, } ) total_prompt_tokens = sum(c.usage.prompt_tokens for c in completions if c) total_completion_tokens = sum( c.usage.completion_tokens for c in completions if c ) total_tokens = sum(c.usage.total_tokens for c in completions if c) last_completion = completions[-1] if completions else None return { "tool_calls": final_tool_calls, "last_completion": last_completion, "usage": { "prompt_tokens": total_prompt_tokens, "completion_tokens": total_completion_tokens, "total_tokens": total_tokens, }, } except Exception as e: print(f"Error processing the tool calls: {e}") raise e class ToolResponseHandler(Handler): async def handle(self, context: Context): body = context.body if context.is_tool_response: messages = context.messages for message in messages: if message["role"] == "tool": message["role"] = "user" message["content"] = get_func_result_guide(message["content"]) try: params = { "temperature": 0.5, "max_tokens": 1024, } params = {**params, **context.params} completion = context.client.route( messages=messages, **context.client.clean_params(params), ) context.response = completion.model_dump() return JSONResponse(content=context.response, status_code=200) except Exception as e: raise e return await super().handle(context) ================================================ FILE: app/libs/vision_handler.py ================================================ from prompts import * from utils import describe from .context import Context from .base_handler import Handler class ImageMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: if message["role"] == "user": if isinstance(message["content"], list): prompt = None for content in message["content"]: if content["type"] == "text": # new_messages.append({"role": message["role"], "content": content["text"]}) prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: prompt = prompt or IMAGE_DESCRIPTO_PROMPT description = describe(prompt, image_url) if description: description = get_image_desc_guide(image_ref, description) new_messages.append( {"role": message["role"], "content": description} ) image_ref += 1 else: pass except Exception as e: print(f"Error describing image: {e}") continue else: new_messages.append(message) else: new_messages.append(message) context.messages = new_messages return await super().handle(context) class ImageLLavaMessageHandler(Handler): async def handle(self, context: Context): new_messages = [] image_ref = 1 for message in context.messages: new_messages.append(message) if message["role"] == "user": if isinstance(message["content"], list): for content in message["content"]: if content["type"] == "text": prompt = content["text"] elif content["type"] == "image_url": image_url = content["image_url"]["url"] try: description = describe(prompt, image_url) new_messages.append( {"role": "assistant", "content": description} ) image_ref += 1 except Exception as e: print(f"Error describing image: {e}") continue context.messages = new_messages return await super().handle(context) ================================================ FILE: app/main.py ================================================ from fastapi import FastAPI from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles from starlette.middleware.cors import CORSMiddleware from starlette.requests import Request from routes import proxy from routes import examples from utils import create_logger import os from dotenv import load_dotenv load_dotenv() app = FastAPI() logger = create_logger("app", ".logs/access.log") app.mount("/static", StaticFiles(directory="frontend/assets"), name="static") templates = Jinja2Templates(directory="frontend/pages") origins = [ "*", ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.middleware("http") async def log_requests(request: Request, call_next): if "/proxy" in request.url.path: client_ip = request.client.host logger.info( f"Incoming request from {client_ip}: {request.method} {request.url}" ) response = await call_next(request) # logger.info(f"Response status code: {response.status_code}") return response else: return await call_next(request) app.include_router(proxy.router, prefix="/proxy") app.include_router(examples.router, prefix="/examples") @app.get("/", response_class=HTMLResponse) async def index(request: Request): return templates.TemplateResponse("index.html", {"request": request}) # Add an get endpoint simple return the evrsion of the app @app.get("/version") async def version(): return {"version": "0.0.5"} if __name__ == "__main__": import uvicorn # uvicorn.run("main:app", host=os.getenv("HOST"), port=int(os.getenv('PORT')), workers=1, reload=True) uvicorn.run( "main:app", host=os.getenv("HOST"), port=int(os.getenv("PORT")), workers=1, reload=False ) ================================================ FILE: app/models.py ================================================ # To be developed ================================================ FILE: app/prompts.py ================================================ 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. A history of conversations between an AI assistant and the user, plus the last user's message, is given to you. In 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. The 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. Depending 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. Your 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. Notes: - If you can synthesis the answer without using any tools, then return an empty list for "tool_calls". - You need tools if there is clear direction between the user's last message and the tools description. - 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. - 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. MESSAGE SUFFIX: - "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. - "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. """ SYSTEM_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. A history of conversations between an AI assistant and the user, plus the last user's message, is given to you. In 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. The 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. Depending 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. Your 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. Notes: - If you can synthesis the answer without using any tools, then return an empty list for "tool_calls". - You need tools if there is clear direction between the user's last message and the tools description. - 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. - 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. MESSAGE SUFFIX: - "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. - "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. """ ENFORCED_SYSTAME_MESSAE = """A history of conversations between an AI assistant and the user, plus the last user's message, is given to you. You 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. Your 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. You should think step by step, provide your reasoning for your response, then add the JSON response at the end following the below schema: { "tool_calls": [{ "name": "function_name", "arguments": { "arg1": "value1", "arg2": "value2", ... }] } } **Wrap the JSON response between ```json and ```, and rememebr "tool_calls" is a list.**. """ CLEAN_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." SUFFIX = """# Example of your response: Here 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. { "tool_calls" : [ { "name": "function_name_1", "arguments": { "arg1" : "value1", "arg2": "value2", ... } }, { "name": "function_name_2", "arguments": { "arg1" : "value1", "arg2": "value2", ... } }, ... ] } **If there is no need for any tools, then return an empty list for "tool_calls", like "{ "tool_calls": [] }".** # Task: Think step by step and justify your response in only two sentences. Remember: - You may select multiple tools if needed. - **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.** - **ONLY USE THE MENTIONED TOOLS ABOVE AND NOTHING OUT OF THAT. Do not suggest a function that is not in the list of tools.** - BE CONCISE AND TO THE POINT. DO NOT ADD ANY UNNECESSARY INFORMATION. MAKER YOUR JUSTIFICATION SHORT. - **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.** - **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.** - **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.** IMPORTANT: 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. # FEW SHOTS: Here we provide some example for you to learn how to generate your response. FEW_SHOTS Make decision based on on the last user message:""" ENFORCE_SUFFIX = """# Example of your response: { "tool_calls" : [{ "name": "function_name", "arguments": { "arg1": "value1", "arg2": "value2", ... }] } NOTE: - **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.** - **ONLY USE THE MENTIONED TOOLS ABOVE AND NOTHING OUT OF THAT. Do not suggest a function that is not in the list of tools.** - BE CONCISE AND TO THE POINT. DO NOT ADD ANY UNNECESSARY INFORMATION. MAKER YOUR JUSTIFICATION SHORT. - **Dont forget to refer to histocy of conversation, when you are trying to figure out values of arguments for the given tool (function).** - **Some time user may have to refer to the previous messages so you can find the argument value from there.** Now extract required data for this tool argument, if any. Make your decision based on on the last user's message:""" TOOLS_OPEN_TOKEN = "" TOOLS_CLOSE_TOKEN = "" FORCE_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 "" and "". Remember to USE THIS JSON WRAPPER ONLY ONE TIME.""" IMAGE_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.""" def get_forced_tool_suffix(tool_name: str) -> str: return FORCE_CALL_SUFFIX.format(tool_name=tool_name) def get_func_result_guide(function_call_result: str) -> str: 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" def get_image_desc_guide(ref_index: int, description: str) -> str: return f"IMAGE: [{ref_index}] : {description}.\n\n" FEW_SHOTS = [ { "input": """# Conversation History: I'm planning a birthday party for my friend. Can you suggest some good catering options? # Available Tools: [ { "name": "get_catering_options", "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.", "parameters": { "type": "object", "properties": { "event_type": { "type": "string" }, "dietary_preferences": { "type": "string" } } } }, { "name": "book_catering", "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.", "parameters": { "type": "object", "properties": { "selected_option": { "type": "string" }, "event_details": { "type": "object" }, "payment_info": { "type": "object" } } } } ]""", "output": """ The 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. { "tool_calls": [ { "name": "get_catering_options", "arguments": { "event_type": "birthday", "dietary_preferences": "" } } ] } """, }, { "input": """# Conversation History: I'm looking for a new laptop for work. Any recommendations? Sure, 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: 1. What is your budget range? 2. What size laptop do you prefer (e.g., 13-inch, 15-inch)? 3. Do you have any specific performance requirements (e.g., processor, RAM, storage)? 4. Are there any particular features you're looking for (e.g., long battery life, lightweight)? Once you provide these details, I can suggest some laptop models that would suit your needs. Thanks 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. # Available Tools: [ { "name": "get_laptop_recommendations", "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.", "parameters": { "type": "object", "properties": { "criteria": { "type": "object" } } } }, { "name": "compare_laptops", "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.", "parameters": { "type": "object", "properties": { "laptop_models": { "type": "array", "items": { "type": "string" } } } } } ]""", "output": """ The 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. { "tool_calls": [ { "name": "get_laptop_recommendations", "arguments": { "criteria": { "budget": 1500, "size": "14-inch or 15-inch", "performance": { "processor": "powerful", "RAM": "at least 16GB" }, "features": { "battery_life": "long" } } } }, { "name": "compare_laptops", "arguments": { "laptop_models": [] } } ] } """, }, { "input": """# Conversation History: I'm planning a trip to Europe next month. Do you have any tips for packing light? Packing light is a great way to make your travel experience more enjoyable and hassle-free. Here are some tips to help you pack efficiently: 1. Choose versatile clothing items that can be mixed and matched to create multiple outfits. 2. Opt for lightweight, quick-drying fabrics that are easy to wash and don't take up much space. 3. Limit your shoes to two or three comfortable pairs that can be worn with different outfits. 4. Use packing cubes or compression bags to organize your belongings and save space in your luggage. 5. Pack travel-sized toiletries and only bring the essentials. 6. Wear your bulkiest items, like jackets or boots, on the plane to save space in your luggage. 7. Consider doing laundry during your trip to reduce the amount of clothing you need to pack. Remember, 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! Thanks 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? # Available Tools: [ { "name": "get_packing_checklist", "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.", "parameters": { "type": "object", "properties": { "destination": { "type": "string" }, "duration": { "type": "string" }, "preferences": { "type": "string" } } } } ]""", "output": """ The 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. { "tool_calls": [] } """, }, { "input": """# Conversation History: Hi what are available comic movies on 23th March? Thank you for providing the information! I found two comic movies showing on March 23rd. Here are the details: 1. "Never Say Never Again" - High octane thrillers, PG, 2 hrs 14 mins, 1983, HD. Airing time: 2:25 PM EDT. 2. "Scary Movie 3" - Slashers, PG-13, 1 hr 25 mins, 2003, HD. Airing time: 11:23 PM EDT. Additionally, I noticed "The Strangers: Prey at Night" is also available on March 23rd if you're interested in a horror movie. - Horror, R, 1 hr 25 mins, 2018, HD. Airing time: 12:03 PM EDT. Please let me know if you'd like to book any of these movies. I'm here to help! Interesting, tell me more about this movie "Never Say Never Again,", what is its plot? # Available Tools: [ { "name": "get_available_slots", "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.", "parameters": { "type": "object", "properties": { "movie_slot_query": { "type": "string" } } } }, { "name": "book_cinema_ticket", "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.", "parameters": { "type": "object", "properties": { "movie_name": { "type": "string" }, "date": { "type": [ "string", "null" ] }, "time": { "type": [ "string", "null" ] }, "user_email": { "type": [ "string", "null" ] } } } }, { "name": "get_chat_history", "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.", "parameters": { "type": "object", "properties": { "num_chats": { "type": [ "number", "null" ] } } } } ]""", "output": """ The 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. { "tool_calls": [] } """, }, { "input": """# Conversation History: What was the closing price of Apple Inc. (AAPL) stock yesterday? # Available Tools: [ { "name": "get_stock_price", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" } } } }, { "name": "get_stock_history", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" }, "period": { "type": "string" } } } } ]""", "output": """ The 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. { "tool_calls": [ { "name": "get_stock_price", "arguments": { "symbol": "AAPL" } } ] } """, }, { "input": """# Conversation History: I'm considering investing in Tesla (TSLA) stock. Can you provide me with some information to help me make a decision? To help you make an informed decision about investing in Tesla (TSLA) stock, I can provide you with the following information: 1. Latest price: The current price of TSLA stock can give you an idea of its market value. 2. 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. 3. Company fundamentals: Researching Tesla's financial statements, revenue growth, profitability, and future projects can provide insights into the company's overall health and potential. 4. Market sentiment: Monitoring news, analyst ratings, and investor sentiment can help gauge the market's perception of Tesla stock. Let me know which specific information you'd like me to provide first, and I'll be happy to assist you further. Thanks 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. # Available Tools: [ { "name": "get_stock_price", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" } } } }, { "name": "get_stock_history", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" }, "period": { "type": "string" } } } } ]""", "output": """ The 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. 1. "get_stock_price" will be used to retrieve the current price of TSLA stock. 2. "get_stock_history" will be used to retrieve the price history of TSLA stock over the past 6 months (period: '6mo'). By combining the results from these two tools, we can give the user the requested information to support their investment decision. { "tool_calls": [ { "name": "get_stock_price", "arguments": { "symbol": "TSLA" } }, { "name": "get_stock_history", "arguments": { "symbol": "TSLA", "period": "6mo" } } ] } """, }, { "input": """# Conversation History: What do you think about the recent performance of the stock market? The 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: 1. Tech stocks: Many technology companies have seen significant gains, driven by strong earnings and optimism about the sector's long-term prospects. 2. Energy sector: Oil and gas stocks have been under pressure due to fluctuating crude oil prices and concerns about the global economic recovery. 3. 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. 4. 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. It'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. Thanks 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. # Available Tools: [ { "name": "get_stock_price", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" } } } }, { "name": "get_stock_history", "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.", "parameters": { "type": "object", "properties": { "symbol": { "type": "string" }, "period": { "type": "string" } } } } ]""", "output": """ The 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. { "tool_calls": [] } """, }, ] import random def get_suffix(): random.shuffle(FEW_SHOTS) # 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 few_shots = "\n\n".join( [ f'-- EXAMPLE {i} ---\nINPUT:\n{example["input"]}\n\nOUTPUT:\n{example["output"]}\n\n' for i, example in enumerate(FEW_SHOTS, 1) ] ) # Replace FEW_SHOTS with the actual examples return SUFFIX.replace("FEW_SHOTS", few_shots) ================================================ FILE: app/providers.py ================================================ from groq import Groq from openai import OpenAI from litellm import completion as sync_call_llm import litellm class BaseProvider: def __init__(self, api_key: str, base_url = None): self.api_key = api_key self.parser_model = "" self.route_model = "" def route(self, model: str, messages: list, **kwargs): pass async def route_async(self, model: str, messages: list, **kwargs): pass def clean_params(self, params): pass class OpenaiProvider(BaseProvider): def __init__(self, api_key: str, base_url = None): super().__init__(api_key) self._client = OpenAI(api_key=api_key) self.parser_model = "gpt-3.5-turbo" self.route_model = "gpt-3.5-turbo" self.exclude_params = ["messages"] def route(self, model: str, messages: list, **kwargs): completion = self._client.chat.completions.create( model=model, messages=messages, **kwargs ) return completion def clean_params(self, params): return {k: v for k, v in params.items() if k not in self.exclude_params} class GroqProvider(BaseProvider): def __init__(self, api_key: str, base_url = None): super().__init__(api_key) self._client = Groq(api_key=api_key) self.parser_model = "mixtral-8x7b-32768" self.route_model = "mixtral-8x7b-32768" self.exclude_params = ["messages", "tools", "tool_choice"] def route(self, model: str, messages: list, **kwargs): completion = self._client.chat.completions.create( model=model, messages=messages, **kwargs ) return completion def clean_params(self, params): return {k: v for k, v in params.items() if k not in self.exclude_params} class OllamaProvider(BaseProvider): def __init__(self, api_key: str, base_url = None): super().__init__(api_key) self.parser_model = "gemma:2b" self.route_model = "gemma:7b" self.exclude_params = ["messages", "tools", "tool_choice"] def route(self, model: str, messages: list, **kwargs): # Filter out all messages with rol assistant and has key "tool_calls" messages = [message for message in messages if message["role"] != "assistant" and "tool_calls" not in message] params = self.clean_params(kwargs) params = { 'max_tokens': 2048, **params } response = sync_call_llm( model=f"ollama/{model}", api_base="http://localhost:11434", messages=messages, **self.clean_params(kwargs) ) return response def clean_params(self, params): return {k: v for k, v in params.items() if k not in self.exclude_params} ================================================ FILE: app/reasoning/__init__.py ================================================ ================================================ FILE: app/reasoning/base.py ================================================ from abc import ABC, abstractmethod from typing import Dict class ReasoningBase(ABC): name: str description: str @abstractmethod def run(self, context) -> Dict: pass ================================================ FILE: app/reasoning/rerank.py ================================================ from pydantic import BaseModel, Field from typing import Optional, Dict from .base import ReasoningBase from pydantic import Field from typing import Optional import requests import json, os from providers import GroqProvider import concurrent.futures from dotenv import load_dotenv load_dotenv() def get_rerank_prompt(query, responses, top_k): 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: {query} Here are the responses generated by different programmers to the user's request: {responses} # Task: Your 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. After selecting the top {top_k} responses, generate a final response by merging and summarizing the selected responses. Format your output as follows: **Make sure to wrap the final answer supposed to be back to user using >>>** # Example of your response: After evaluating the responses, I have selected the top {top_k} that best address the user's request. Here they are: summary of response 1 summary of response 2 ... summary of response top_k Based on these top {top_k} responses, I have generated a final response by merging and summarizing them: >>> final_response_for_user >>>""" return prompt class RerankReasoning(ReasoningBase): name = "rerank" description = "Use this reasoning strategy to generate and rerank responses to a user query." def __init__(self, generator_model: str, reranker_model: str, n: int = 5, top_k: int = 3): self.generator_model = generator_model self.reranker_model = reranker_model self.n = n self.top_k = top_k self.generator_provider = GroqProvider(api_key=os.getenv("GROQ_API_KEY")) self.reranker_provider = GroqProvider(api_key=os.getenv("GROQ_API_KEY")) def run(self, context): message_stories = context.messages # Generate responses in parallel using a thread pool with concurrent.futures.ThreadPoolExecutor() as executor: response_futures = [executor.submit(self.generator_provider.route, model=self.generator_model, messages=message_stories) for _ in range(self.n)] responses = [future.result().get("response") for future in concurrent.futures.as_completed(response_futures)] unique_responses = list(set(responses)) # Adjust top_k if it's greater than the number of unique responses if self.top_k > len(unique_responses): self.top_k = int(len(unique_responses) * 0.4) # Generate prompt for reranking prompt = get_rerank_prompt( query=message_stories[-1].content, responses='\n\n'.join([f'\n{response}\n' for idx, response in enumerate(unique_responses)]), top_k=self.top_k ) # Rerank responses rerank_completion = self.reranker_provider.route(model=self.reranker_model, messages=[{"content": prompt}]) reranked_response = rerank_completion.get("response") # Extract the final response final_response = reranked_response.split(">>>")[1].split(">>>")[0].strip() # Add the final response as a new message to the context new_message = {"role": "assistant", "content":final_response} context.messages.append(new_message) return new_message ================================================ FILE: app/routes/__init__.py ================================================ from .proxy import router as proxy_router from .examples import router as examples_router __all__ = [ "proxy_router", "examples_router" ] ================================================ FILE: app/routes/examples.py ================================================ from fastapi.responses import FileResponse from fastapi import APIRouter import os router = APIRouter() # Add endpoint to downl;oad files in the ../examples folder @router.get("/{file_path}") async def read_examples(file_path: str): # get parent directory parent = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) file_path = f"{parent}/examples/{file_path}" if os.path.exists(file_path): return FileResponse(file_path) else: return {"error": "File not found."} # @router.get("/examples") # async def read_root(): # return {"message": "Hello World", "examples": [ # "/example/example_1.py", # "/example/example_2.py", # "/example/example_3.py", # "/example/example_4.py", # ]} ================================================ FILE: app/routes/proxy.py ================================================ from fastapi import APIRouter, Response, Request, Path, Query from fastapi.responses import JSONResponse # from libs.chains import ( # Context, # ProviderSelectionHandler, # ImageMessageHandler, # ToolExtractionHandler, # ToolResponseHandler, # DefaultCompletionHandler, # FallbackHandler, # ) from libs import ( Context, ProviderSelectionHandler, ImageMessageHandler, ToolExtractionHandler, ToolResponseHandler, DefaultCompletionHandler, FallbackHandler, ) from typing import Optional router = APIRouter() # Add get endpoint for /openai/v1 and print request body @router.get("/{provider}/v1") async def get_openai_v1( response: Response, provider: str = Path(..., title="Provider") ) -> JSONResponse: return JSONResponse(content={"message": f"GET request to {provider} v1"}) @router.post("/groqchain/{provider}/v1/chat/completions") async def post_groq_chat_completions( request: Request, provider: str = Path(..., title="Provider") ) -> JSONResponse: # Call the original post_chat_completions method with provider set to "groq" return await post_chat_completions(request, provider="groq") @router.post("/{provider}/v1/chat/completions") async def post_chat_completions( request: Request, provider: str = Path(..., title="Provider") ) -> JSONResponse: try: if not provider: provider = "openai" if not ProviderSelectionHandler.provider_exists(provider): return JSONResponse(content={"error": "Invalid provider"}, status_code=400) # Extract the API token and body from the request api_token = request.headers.get("Authorization").split("Bearer ")[1] body = await request.json() # Initialize the context with request details context = Context(request, provider, body) context.api_token = ( api_token # Adding the API token to the context for use in handlers ) # Initialize and link the handlers provider_selection_handler = ProviderSelectionHandler() image_message_handler = ImageMessageHandler() tool_extraction_handler = ToolExtractionHandler() tool_response_handler = ToolResponseHandler() default_completion_handler = DefaultCompletionHandler() fallback_handler = FallbackHandler() # Set up the chain of responsibility chains = [ provider_selection_handler, image_message_handler, tool_extraction_handler, tool_response_handler, default_completion_handler, fallback_handler, ] for i in range(len(chains) - 1): chains[i].set_next(chains[i + 1]) # provider_selection_handler.set_next(tool_extraction_handler).set_next( # tool_response_handler # ).set_next(default_completion_handler).set_next(fallback_handler) # Execute the chain with the initial context response = await provider_selection_handler.handle(context) # Return the response generated by the handlers return response except Exception as e: print(f"Error processing the request: {e}") return JSONResponse( content={"error": "An unexpected error occurred"}, status_code=500 ) ================================================ FILE: app/utils.py ================================================ import logging import os import replicate import base64 from io import BytesIO # To be developed def create_logger(logger_name: str, log_path: str = ".logs/access.log", show_on_shell: bool = False): log_dir = os.path.dirname(log_path) if not os.path.exists(log_dir): os.makedirs(log_dir) logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG) file_handler = logging.FileHandler(log_path) file_handler.setLevel(logging.DEBUG) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) file_handler.setFormatter(formatter) logger.addHandler(file_handler) if show_on_shell: stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.DEBUG) shell_formatter = logging.Formatter( "%(levelname)s (%(name)s) %(message)s" ) stream_handler.setFormatter(shell_formatter) logger.addHandler(stream_handler) return logger def get_tool_call_response(tool_calls_result, unresolved_tol_calls, resolved_responses): last_completion = tool_calls_result["last_completion"] tool_response = { "id": "chatcmpl-" + last_completion.id if last_completion else None, "object": "chat.completion", "created": last_completion.created if last_completion else None, "model": last_completion.model if last_completion else None, "choices": [ { "index": 0, "message": { "role": "assistant", "content": "", # None, "tool_calls": unresolved_tol_calls, }, "logprobs": None, "finish_reason": "tool_calls", } ], "resolved": resolved_responses, "usage": tool_calls_result["usage"], "system_fingerprint": last_completion.system_fingerprint if last_completion else None, } return tool_response def describe(prompt: str, image_url_or_base64 : str, **kwargs) -> str: logger = create_logger("vision", ".logs/access.log", True) try: if image_url_or_base64.startswith("data:image/"): # If the input is a base64 string image_data = base64.b64decode(image_url_or_base64.split(",")[1]) image_file = BytesIO(image_data) else: # If the input is a URL image_file = image_url_or_base64 model_params = { "top_p": 1, "max_tokens": 1024, "temperature": 0.2 } model_params.update(kwargs) logger.info("Running the model") output = replicate.run( "yorickvp/llava-13b:01359160a4cff57c6b7d4dc625d0019d390c7c46f553714069f114b392f4a726", input={ "image": image_file, "prompt": prompt, #"Describe the image in detail.", **model_params } ) description = "" for item in output: if not description: logger.info("Streaming...") description += item return description.strip() except Exception as e: logger.error( f"Vision model, An error occurred: {e}") return None # describe("Describe the image in detail.", "https://replicate.delivery/pbxt/KRULC43USWlEx4ZNkXltJqvYaHpEx2uJ4IyUQPRPwYb8SzPf/view.jpg") ================================================ FILE: cookbook/ai_assistant_custome_tools.py ================================================ import os, json from typing import Optional, List from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.knowledge.json import JSONKnowledgeBase from phi.vectordb.pgvector import PgVector2 from phi.storage.assistant.postgres import PgAssistantStorage from phi.tools import Toolkit from phi.tools.email import EmailTools from phi.utils.log import logger from phi.tools.email import EmailTools from phi.knowledge.base import AssistantKnowledge from phi.knowledge.base import Document from resources import vector_db from rich.prompt import Prompt from dotenv import load_dotenv load_dotenv() # To run this example, first make sure to follow the instructions below: # 1. Install the phidata: pip install phidata # 2. Run the following command to start a docker, with pgvector db running: phi start resources.py # 3. Download the sample of JSON knowledge base from the same folder of this file: cinemax.json class CinemaSerachDB(Toolkit): def __init__( self, knowledge_base : Optional[AssistantKnowledge] = None, num_documents: int = None ): super().__init__(name="get_available_slots") self.knowledge_base = knowledge_base self.num_documents = num_documents self.register(self.get_available_slots) def get_available_slots(self, movie_slot_query: str ) -> str: """Use this function to search the Cinemax database of available movies, show time, and date. :param query: The query to search the Cinemax database of available movies, show time, and date. :return: A string containing the response to the query. """ relevant_docs: List[Document] = self.knowledge_base.search(query=movie_slot_query, num_documents=self.num_documents) if len(relevant_docs) == 0: return None return json.dumps([doc.to_dict() for doc in relevant_docs], indent=2) class CinemaTools(Toolkit): def __init__( self, email_tools: Optional["EmailTools"] = None, ): super().__init__(name="cinema_tools") self.email_tools = email_tools self.register(self.book_cinema_ticket) def book_cinema_ticket(self, movie_name: str, date: Optional[str] = None, time: Optional[str] = None, user_email: Optional[str] = None) -> str: """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 Args: movie_name (str): The name of the movie. date (Optional[str], optional): The date of the movie. time (Optional[str], optional): The time of the movie. user_email (Optional[str], optional): The email of the user. Defaults to None. Returns: The result of the operation. """ anything_missed = any([not movie_name, not date, not time, not user_email]) missed_items = [] if anything_missed: if not date: missed_items.append( "error: No date provided, I need a date to book a ticket") if not time: missed_items.append( "error: No time provided, I need a time to book a ticket") if not user_email: missed_items.append( "error: No user email provided, I need an email to send the ticket") missed_itemes = ", ".join(missed_items) return f"There are some missing items: \n{missed_itemes}" # Simulate booking the ticket ticket_number = self._generate_ticket_number() logger.info(f"Booking ticket for {movie_name} on {date} at {time}") # Prepare the email subject and body subject = f"Your ticket for {movie_name}" body = f"Dear user,\n\nYour ticket for {movie_name} on {date} at {time} has been booked.\n\n" \ f"Your ticket number is: {ticket_number}\n\nEnjoy the movie!\n\nBest regards,\nThe Cinema Team" # Send the email using the EmailTools if not self.email_tools: return "error: No email tools provided" self.email_tools.receiver_email = user_email result = self.email_tools.email_user(subject, body) if result.startswith("error"): logger.error(f"Error booking ticket: {result}") return result return "success" def _generate_ticket_number(self) -> str: """Generates a dummy ticket number.""" import random import string return "".join(random.choices(string.ascii_uppercase + string.digits, k=10)) kb = JSONKnowledgeBase( path="cinemax.json", vector_db=PgVector2(collection="cinemax", db_url=vector_db.get_db_connection_local()), ) storage = PgAssistantStorage( table_name="cinemax_assistant_storage", db_url=vector_db.get_db_connection_local(), ) my_groq = OpenAILike( model="mixtral-8x7b-32768", api_key=os.environ["GROQ_API_KEY"], base_url="http://localhost:8000/proxy/groq/v1" # base_url="http://groqcall.ai/proxy/groq/v1" ) def cinemax_assistant(new: bool = False, user: str = "user"): run_id: Optional[str] = None # new = False # new = True user_id = user if not new: existing_run_ids: List[str] = storage.get_all_run_ids(user_id) if len(existing_run_ids) > 0: run_id = existing_run_ids[0] assistant = Assistant( run_id=run_id, user_id="test_user", llm=my_groq, # knowledge_base=kb, Since I created my own tool CinemaSerachDB storage=storage, use_tools=True, tools=[ CinemaSerachDB(knowledge_base=kb, num_documents=5), CinemaTools(EmailTools("YOUR_EMAIL_ADDRESS", "SENDER_NAME", "SENDER_EMAIL", os.environ['email_pass_key'] )) ], show_tool_calls=True, markdown=True, add_chat_history_to_messages=True, # Inject history in every prompt add_datetime_to_instructions=True, limit_tool_access=True, extra_instructions="", output_model= None, # Pydantic prevent_prompt_injection=True, add_references_to_prompt=False, # It searches in the kb and add it to every prompt # add_references_to_prompt=True, 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." ) assistant.knowledge_base.load(recreate=False) if run_id is None: run_id = assistant.run_id print(f"Started Run: {run_id}\n") else: print(f"Continuing Run: {run_id}\n") while True: message = Prompt.ask(f"[bold] :sunglasses: {user} [/bold]") if message in ("exit", "bye"): break assistant.print_response(message, markdown=True, stream=False) # response = assistant.run(message, stream=False) if __name__ == "__main__": cinemax_assistant(user="Tom") ================================================ FILE: cookbook/cinemax.json ================================================ [ { "date": "March 17", "name": "Barbershop 2: Back in Business", "airing_time": "10:37 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']" }, { "date": "March 17", "name": "Hustle & Flow", "airing_time": "12:24 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']" }, { "date": "March 17", "name": "Predators", "airing_time": "2:21 PM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 49 mins|', '2010|', 'HD|']" }, { "date": "March 17", "name": "Paranoia", "airing_time": "4:08 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']" }, { "date": "March 17", "name": "XXX", "airing_time": "5:55 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 6 mins|', '2002|', 'HD|']" }, { "date": "March 17", "name": "Black Mass", "airing_time": "8:00 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "Freedomland", "airing_time": "10:03 PM EDT", "details_spans": "['big screen crime|', 'R|', '1 hr 56 mins|', '2006|', 'HD|']" }, { "date": "March 17", "name": "Attack the Block", "airing_time": "11:57 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 30 mins|', '2011|']" }, { "date": "March 17", "name": "Fifty Shades of Grey", "airing_time": "1:26 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "A Prayer Before Dawn", "airing_time": "3:32 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 58 mins|', '2018|']" }, { "date": "March 17", "name": "The Pick-Up Artist", "airing_time": "5:30 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 22 mins|', '1987|']" }, { "date": "March 17", "name": "I Am Wrath", "airing_time": "8:20 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 32 mins|', '2016|']" }, { "date": "March 17", "name": "Leatherheads", "airing_time": "9:51 AM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 17", "name": "Office Space", "airing_time": "11:45 AM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 31 mins|', '1999|', 'HD|']" }, { "date": "March 17", "name": "Paycheck", "airing_time": "1:15 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs|', '2003|', 'HD|']" }, { "date": "March 17", "name": "The Sea of Trees", "airing_time": "11:19 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 52 mins|', '2016|']" }, { "date": "March 17", "name": "Fx2: The Deadly Art of Illusion", "airing_time": "1:11 PM EDT", "details_spans": "['mystery & suspense|', 'PG-13|', '1 hr 50 mins|', '1991|', 'HD|']" }, { "date": "March 17", "name": "Scary Movie", "airing_time": "3:00 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 30 mins|', '2000|', 'HD|']" }, { "date": "March 17", "name": "Scary Movie 2", "airing_time": "4:29 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']" }, { "date": "March 17", "name": "Scary Movie 3", "airing_time": "5:52 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 26 mins|', '2003|', 'HD|']" }, { "date": "March 17", "name": "Jennifer's Body", "airing_time": "7:17 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 44 mins|', '2009|', 'HD|']" }, { "date": "March 17", "name": "Friday the 13th", "airing_time": "9:00 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 39 mins|', '2009|', 'HD|']" }, { "date": "March 17", "name": "The Witch", "airing_time": "10:38 PM EDT", "details_spans": "['occult|', 'R|', '1 hr 34 mins|', '2016|', 'HD|']" }, { "date": "March 17", "name": "Dark Places", "airing_time": "12:11 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 55 mins|', '2015|']" }, { "date": "March 17", "name": "Conviction", "airing_time": "2:05 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 49 mins|', '2010|']" }, { "date": "March 17", "name": "Pride and Glory", "airing_time": "3:53 AM EDT", "details_spans": "['drama|', 'NR|', '2 hrs 11 mins|', '2008|']" }, { "date": "March 17", "name": "Master of the Shadowless Kick: Wong Kei-Ying", "airing_time": "7:39 AM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 40 mins|', '2017|']" }, { "date": "March 17", "name": "The New World", "airing_time": "9:18 AM EDT", "details_spans": "['drama|', 'NR|', '2 hrs 17 mins|', '2005|']" }, { "date": "March 17", "name": "Body of Lies", "airing_time": "11:34 AM EDT", "details_spans": "['action|', 'NR|', '2 hrs 9 mins|', '2008|']" }, { "date": "March 17", "name": "Barbershop", "airing_time": "11:00 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 44 mins|', '2002|', 'HD|']" }, { "date": "March 17", "name": "Beauty Shop", "airing_time": "12:43 PM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 47 mins|', '2005|', 'HD|']" }, { "date": "March 17", "name": "Taxi", "airing_time": "2:29 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 38 mins|', '2004|']" }, { "date": "March 17", "name": "Talk Black", "airing_time": "4:07 PM EDT", "details_spans": "['comedy|', 'TV-14|', '15 mins|', '2023|', 'HD|']" }, { "date": "March 17", "name": "The Company You Keep", "airing_time": "4:21 PM EDT", "details_spans": "['suspense|', 'NR|', '2 hrs 3 mins|', '2013|']" }, { "date": "March 17", "name": "Pure", "airing_time": "6:23 PM EDT", "details_spans": "['romantic dramas|', 'TV-14|', '14 mins|', '2022|', 'HD|']" }, { "date": "March 17", "name": "Extortion", "airing_time": "6:36 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 50 mins|', '2017|']" }, { "date": "March 17", "name": "Hotel Artemis", "airing_time": "8:26 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 34 mins|', '2018|']" }, { "date": "March 17", "name": "House at the End of the Street", "airing_time": "10:00 PM EDT", "details_spans": "['horror|', 'NR|', '1 hr 41 mins|', '2012|']" }, { "date": "March 17", "name": "Attack the Block", "airing_time": "11:41 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 30 mins|', '2011|']" }, { "date": "March 17", "name": "Line of Duty", "airing_time": "1:10 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 42 mins|', '2013|']" }, { "date": "March 17", "name": "Poltergeist II: The Other Side", "airing_time": "2:51 AM EDT", "details_spans": "['horror|', 'NR|', '1 hr 32 mins|', '1986|']" }, { "date": "March 17", "name": "Poltergeist III", "airing_time": "4:22 AM EDT", "details_spans": "['horror|', 'NR|', '1 hr 39 mins|', '1988|']" }, { "date": "March 17", "name": "Yes Man", "airing_time": "7:31 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 17", "name": "Missing in Action", "airing_time": "9:15 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 43 mins|', '1984|']" }, { "date": "March 17", "name": "Braddock: Missing in Action III", "airing_time": "10:57 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 45 mins|', '1988|']" }, { "date": "March 17", "name": "Unbreakable", "airing_time": "12:41 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 48 mins|', '2000|', 'HD|']" }, { "date": "March 17", "name": "The Frozen Ground", "airing_time": "10:18 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 46 mins|', '2013|']" }, { "date": "March 17", "name": "Antitrust", "airing_time": "12:04 PM EDT", "details_spans": "['adventure|', 'PG-13|', '1 hr 50 mins|', '2001|', 'HD|']" }, { "date": "March 17", "name": "Charlie Wilson's War", "airing_time": "1:53 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2007|', 'HD|']" }, { "date": "March 17", "name": "Skin Can Breathe", "airing_time": "3:35 PM EDT", "details_spans": "['drama|', 'TV-14|', '12 mins|', '2022|', 'HD|']" }, { "date": "March 17", "name": "Gemini", "airing_time": "3:47 PM EDT", "details_spans": "['drama|', 'NR|', '1 hr 35 mins|', '2018|']" }, { "date": "March 17", "name": "The Strangers: Prey at Night", "airing_time": "5:21 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']" }, { "date": "March 17", "name": "Mojave", "airing_time": "6:47 PM EDT", "details_spans": "['drama|', 'NR|', '1 hr 36 mins|', '2016|']" }, { "date": "March 17", "name": "Fargo", "airing_time": "8:21 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 40 mins|', '1996|', 'HD|']" }, { "date": "March 17", "name": "Victor Frankenstein", "airing_time": "10:00 PM EDT", "details_spans": "['horror|', 'NR|', '1 hr 51 mins|', '2015|']" }, { "date": "March 17", "name": "Revenge of the Green Dragons", "airing_time": "11:50 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 36 mins|', '2014|']" }, { "date": "March 17", "name": "The Forbidden Kingdom", "airing_time": "1:25 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 46 mins|', '2008|']" }, { "date": "March 17", "name": "Green Room", "airing_time": "3:10 AM EDT", "details_spans": "['horror|', 'NR|', '1 hr 36 mins|', '2016|']" }, { "date": "March 17", "name": "Into the Blue 2: The Reef", "airing_time": "4:46 AM EDT", "details_spans": "['suspense|', 'TV-MA|', '1 hr 33 mins|', '2009|']" }, { "date": "March 17", "name": "Altitude", "airing_time": "8:10 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 29 mins|', '2017|']" }, { "date": "March 17", "name": "Friday the 13th", "airing_time": "9:38 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']" }, { "date": "March 17", "name": "Friday the 13th, Part II", "airing_time": "11:14 AM EDT", "details_spans": "['slashers|', 'R|', '1 hr 27 mins|', '1981|', 'HD|']" }, { "date": "March 17", "name": "Friday the 13th Part III", "airing_time": "12:41 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 38 mins|', '1982|', 'HD|']" }, { "date": "March 17", "name": "Woman Walks Ahead", "airing_time": "10:45 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 44 mins|', '2018|']" }, { "date": "March 17", "name": "Winter's Tale", "airing_time": "12:28 PM EDT", "details_spans": "['fantasy-sci-fi|', 'NR|', '1 hr 59 mins|', '2014|']" }, { "date": "March 17", "name": "The Dead Don't Die", "airing_time": "2:26 PM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 46 mins|', '2019|']" }, { "date": "March 17", "name": "Paycheck", "airing_time": "4:11 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 2 mins|', '2003|', 'HD|']" }, { "date": "March 17", "name": "The Pick-Up Artist", "airing_time": "6:11 PM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 23 mins|', '1987|']" }, { "date": "March 17", "name": "Buffy the Vampire Slayer", "airing_time": "7:34 PM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 27 mins|', '1992|', 'HD|']" }, { "date": "March 17", "name": "Observe and Report", "airing_time": "9:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']" }, { "date": "March 17", "name": "Pens & Pencils", "airing_time": "10:27 PM EDT", "details_spans": "['mystery & suspense|', 'TV-14|', '17 mins|', '2023|', 'HD|']" }, { "date": "March 17", "name": "Vox Lux", "airing_time": "10:43 PM EDT", "details_spans": "['drama|', 'NR|', '1 hr 55 mins|', '2018|']" }, { "date": "March 17", "name": "A Glimpse Inside the Mind of Charles Swan III", "airing_time": "12:38 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 26 mins|', '2013|']" }, { "date": "March 17", "name": "Missing in Action 2 - The Beginning", "airing_time": "2:04 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 37 mins|', '1985|']" }, { "date": "March 17", "name": "Major League", "airing_time": "3:40 AM EDT", "details_spans": "['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']" }, { "date": "March 17", "name": "Cyrus", "airing_time": "7:38 AM EDT", "details_spans": "['romance|', 'NR|', '1 hr 32 mins|', '2010|']" }, { "date": "March 17", "name": "Laggies", "airing_time": "9:09 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 41 mins|', '2014|']" }, { "date": "March 17", "name": "Poltergeist (2015)", "airing_time": "10:49 AM EDT", "details_spans": "['fantasy-sci-fi|', 'NR|', '1 hr 35 mins|', '2015|']" }, { "date": "March 17", "name": "Room", "airing_time": "12:23 PM EDT", "details_spans": "['family relationships|', 'R|', '1 hr 59 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "Never Say Never Again", "airing_time": "10:07 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 16 mins|', '1983|', 'HD|']" }, { "date": "March 17", "name": "Mississippi Grind", "airing_time": "12:22 PM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 50 mins|', '2015|']" }, { "date": "March 17", "name": "Dumb and Dumber To", "airing_time": "2:12 PM EDT", "details_spans": "['dark comedy|', 'PG-13|', '1 hr 51 mins|', '2014|', 'HD|']" }, { "date": "March 17", "name": "Appaloosa", "airing_time": "4:02 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 57 mins|', '2008|', 'HD|']" }, { "date": "March 17", "name": "Live and Let Die", "airing_time": "5:58 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 2 mins|', '1973|', 'HD|']" }, { "date": "March 17", "name": "Across the Universe", "airing_time": "8:00 PM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']" }, { "date": "March 17", "name": "Wonderland", "airing_time": "10:14 PM EDT", "details_spans": "['drama|', 'NR|', '1 hr 45 mins|', '2003|']" }, { "date": "March 17", "name": "The Last Movie Star", "airing_time": "11:59 PM EDT", "details_spans": "['drama|', 'NR|', '1 hr 44 mins|', '2018|']" }, { "date": "March 17", "name": "Walk the Line", "airing_time": "1:43 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '2 hrs 16 mins|', '2005|', 'HD|']" }, { "date": "March 17", "name": "Lean on Pete", "airing_time": "3:58 AM EDT", "details_spans": "['drama|', '12|', '2 hrs 4 mins|', '2018|', 'HD|']" }, { "date": "March 17", "name": "Curse of the Pink Panther", "airing_time": "8:06 AM EDT", "details_spans": "['offbeat|', 'PG|', '1 hr 52 mins|', '1983|', 'HD|']" }, { "date": "March 17", "name": "Ronin", "airing_time": "9:57 AM EDT", "details_spans": "['action|', 'NR|', '2 hrs 3 mins|', '1998|']" }, { "date": "March 17", "name": "De-Lovely", "airing_time": "11:58 AM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 7 mins|', '2004|', 'HD|']" }, { "date": "March 17", "name": "XXX: State of the Union", "airing_time": "11:26 AM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 17", "name": "Fifty Shades Darker", "airing_time": "1:07 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 17", "name": "Poltergeist (2015)", "airing_time": "3:04 PM EDT", "details_spans": "['fantasy-sci-fi|', 'NR|', '1 hr 34 mins|', '2015|']" }, { "date": "March 17", "name": "Yes Man", "airing_time": "4:38 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 17", "name": "I Love You, Beth Cooper", "airing_time": "6:23 PM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 42 mins|', '2009|']" }, { "date": "March 17", "name": "The Vanishing of Sidney Hall", "airing_time": "8:05 PM EDT", "details_spans": "['drama|', 'NR|', '2 hrs|', '2018|']" }, { "date": "March 17", "name": "Laggies", "airing_time": "10:05 PM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 40 mins|', '2014|']" }, { "date": "March 17", "name": "Cracked", "airing_time": "11:45 PM EDT", "details_spans": "['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']" }, { "date": "March 17", "name": "Strange Days", "airing_time": "12:00 AM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 17", "name": "Kingsman: The Secret Service", "airing_time": "2:26 AM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "Johnny Dangerously", "airing_time": "4:35 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']" }, { "date": "March 17", "name": "The Goods: Live Hard, Sell Hard", "airing_time": "7:41 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 31 mins|', '2009|']" }, { "date": "March 17", "name": "John Dies at the End", "airing_time": "9:11 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 41 mins|', '2013|', 'HD|']" }, { "date": "March 17", "name": "Don't Let Go", "airing_time": "10:51 AM EDT", "details_spans": "['horror|', 'NR|', '1 hr 44 mins|', '2019|']" }, { "date": "March 17", "name": "Unmothered", "airing_time": "12:35 PM EDT", "details_spans": "['dark comedy|', 'TV-PG|', '16 mins|', '2021|', 'HD|']" }, { "date": "March 17", "name": "Strange Days", "airing_time": "12:50 PM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 27 mins|', '1995|', 'HD|']" }, { "date": "March 17", "name": "Barbershop 2: Back in Business", "airing_time": "10:37 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']" }, { "date": "March 17", "name": "Hustle & Flow", "airing_time": "12:24 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']" }, { "date": "March 17", "name": "Predators", "airing_time": "2:21 PM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 49 mins|', '2010|', 'HD|']" }, { "date": "March 17", "name": "Paranoia", "airing_time": "4:08 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']" }, { "date": "March 17", "name": "XXX", "airing_time": "5:55 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 6 mins|', '2002|', 'HD|']" }, { "date": "March 17", "name": "Black Mass", "airing_time": "8:00 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "Freedomland", "airing_time": "10:03 PM EDT", "details_spans": "['big screen crime|', 'R|', '1 hr 56 mins|', '2006|', 'HD|']" }, { "date": "March 17", "name": "Attack the Block", "airing_time": "11:57 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 30 mins|', '2011|']" }, { "date": "March 17", "name": "Fifty Shades of Grey", "airing_time": "1:26 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 17", "name": "A Prayer Before Dawn", "airing_time": "3:32 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 58 mins|', '2018|']" }, { "date": "March 17", "name": "The Pick-Up Artist", "airing_time": "5:30 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 22 mins|', '1987|']" }, { "date": "March 17", "name": "I Am Wrath", "airing_time": "8:20 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 32 mins|', '2016|']" }, { "date": "March 17", "name": "Leatherheads", "airing_time": "9:51 AM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 17", "name": "Office Space", "airing_time": "11:45 AM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 31 mins|', '1999|', 'HD|']" }, { "date": "March 17", "name": "Paycheck", "airing_time": "1:15 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs|', '2003|', 'HD|']" }, { "date": "March 18", "name": "Taxi", "airing_time": "11:24 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 38 mins|', '2004|']" }, { "date": "March 18", "name": "Pieces of April", "airing_time": "1:02 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 21 mins|', '2003|']" }, { "date": "March 18", "name": "Never Goin' Back", "airing_time": "2:22 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 28 mins|', '2018|']" }, { "date": "March 18", "name": "Life After Beth", "airing_time": "3:49 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 30 mins|', '2014|', 'HD|']" }, { "date": "March 18", "name": "Equals", "airing_time": "5:18 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 43 mins|', '2016|']" }, { "date": "March 18", "name": "Fifty Shades Darker", "airing_time": "7:00 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 59 mins|', '2017|', 'HD|']" }, { "date": "March 18", "name": "Rescue Dawn", "airing_time": "8:58 AM EDT", "details_spans": "['action|', 'NR|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 18", "name": "Election", "airing_time": "11:04 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']" }, { "date": "March 18", "name": "How to Talk to Girls at Parties", "airing_time": "12:47 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']" }, { "date": "March 18", "name": "Barbershop", "airing_time": "2:30 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 18", "name": "Yes Man", "airing_time": "4:13 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 18", "name": "The Company You Keep", "airing_time": "5:58 PM EDT", "details_spans": "['suspense|', '2 hrs 2 mins|', '2013|']" }, { "date": "March 18", "name": "Unbreakable", "airing_time": "8:00 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 18", "name": "Kingsman: The Secret Service", "airing_time": "9:47 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Blue Valentine", "airing_time": "11:56 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 18", "name": "A Most Violent Year", "airing_time": "11:07 PM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Little Woods", "airing_time": "1:12 AM EDT", "details_spans": "['suspense|', 'NR|', '1 hr 46 mins|', '2019|']" }, { "date": "March 18", "name": "Source Code", "airing_time": "2:56 AM EDT", "details_spans": "['suspense|', 'NR|', '1 hr 35 mins|', '2011|']" }, { "date": "March 18", "name": "Spaceballs", "airing_time": "4:30 AM EDT", "details_spans": "['fantasy & sci-fi|', 'PG|', '1 hr 37 mins|', '1987|', 'HD|']" }, { "date": "March 18", "name": "The Fluffy Movie", "airing_time": "6:07 AM EDT", "details_spans": "['documentary|', 'NR|', '1 hr 44 mins|', '2014|']" }, { "date": "March 18", "name": "Leap of Faith", "airing_time": "7:49 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 50 mins|', '1992|']" }, { "date": "March 18", "name": "The Last Word", "airing_time": "9:37 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 50 mins|', '2017|']" }, { "date": "March 18", "name": "The Answer Man", "airing_time": "11:25 AM EDT", "details_spans": "['romantic comedy|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']" }, { "date": "March 18", "name": "My Boss\u2019s Daughter", "airing_time": "1:03 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 27 mins|', '2003|', 'HD|']" }, { "date": "March 18", "name": "A Night at the Roxbury", "airing_time": "2:30 PM EDT", "details_spans": "['offbeat|', 'PG-13|', '1 hr 22 mins|', '1998|', 'HD|']" }, { "date": "March 18", "name": "Trail of the Pink Panther", "airing_time": "3:52 PM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 37 mins|', '1982|', 'HD|']" }, { "date": "March 18", "name": "Whiteout", "airing_time": "5:29 PM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2009|']" }, { "date": "March 18", "name": "Victor Frankenstein", "airing_time": "7:10 PM EDT", "details_spans": "['horror|', '1 hr 50 mins|', '2015|']" }, { "date": "March 18", "name": "Evan Almighty", "airing_time": "9:00 PM EDT", "details_spans": "['kids & family|', 'PG|', '1 hr 36 mins|', '2007|', 'HD|']" }, { "date": "March 18", "name": "Dumb and Dumber To", "airing_time": "10:36 PM EDT", "details_spans": "['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']" }, { "date": "March 18", "name": "Hot Summer Nights", "airing_time": "12:26 AM EDT", "details_spans": "['comedy|', '1 hr 49 mins|', '2018|']" }, { "date": "March 18", "name": "Missing in Action", "airing_time": "11:45 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 43 mins|', '1984|']" }, { "date": "March 18", "name": "Teen Wolf", "airing_time": "1:27 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 18", "name": "Paparazzi", "airing_time": "3:00 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 26 mins|', '2004|']" }, { "date": "March 18", "name": "American Heist", "airing_time": "4:25 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 37 mins|', '2015|']" }, { "date": "March 18", "name": "Missing in Action 2 - The Beginning", "airing_time": "6:01 AM EDT", "details_spans": "['action|', 'NR|', '1 hr 36 mins|', '1985|']" }, { "date": "March 18", "name": "Predators", "airing_time": "7:37 AM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']" }, { "date": "March 18", "name": "Children of the Corn II: The Final Sacrifice", "airing_time": "9:24 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 36 mins|', '1993|', 'HD|']" }, { "date": "March 18", "name": "Brokedown Palace", "airing_time": "10:58 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 43 mins|', '1999|']" }, { "date": "March 18", "name": "The Rage: Carrie 2", "airing_time": "12:39 PM EDT", "details_spans": "['occult|', 'R|', '1 hr 45 mins|', '1999|', 'HD|']" }, { "date": "March 18", "name": "The Edge", "airing_time": "2:24 PM EDT", "details_spans": "['suspense|', '1 hr 58 mins|', '1997|']" }, { "date": "March 18", "name": "Major League", "airing_time": "4:22 PM EDT", "details_spans": "['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']" }, { "date": "March 18", "name": "Pens & Pencils", "airing_time": "6:09 PM EDT", "details_spans": "['mystery & suspense|', 'TV-14|', '16 mins|', '2023|', 'HD|']" }, { "date": "March 18", "name": "XXX: State of the Union", "airing_time": "6:25 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 18", "name": "Leatherheads", "airing_time": "8:06 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 18", "name": "Attack the Block", "airing_time": "10:00 PM EDT", "details_spans": "['action|', '1 hr 29 mins|', '2011|']" }, { "date": "March 18", "name": "Don't Let Go", "airing_time": "11:29 PM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 18", "name": "Extortion", "airing_time": "1:13 AM EDT", "details_spans": "['action|', '1 hr 50 mins|', '2017|']" }, { "date": "March 18", "name": "Blown Away", "airing_time": "11:38 PM EDT", "details_spans": "['drama|', 'R|', '2 hrs 2 mins|', '1994|', 'HD|']" }, { "date": "March 18", "name": "The Fury", "airing_time": "1:39 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 59 mins|', '1978|', 'HD|']" }, { "date": "March 18", "name": "Chain Reaction", "airing_time": "3:38 AM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']" }, { "date": "March 18", "name": "Internal Affairs", "airing_time": "5:25 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 55 mins|', '1990|']" }, { "date": "March 18", "name": "The Gift", "airing_time": "7:20 AM EDT", "details_spans": "['drama|', 'TV-14|', '16 mins|', '2022|', 'HD|']" }, { "date": "March 18", "name": "Charlie Wilson's War", "airing_time": "7:35 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2007|', 'HD|']" }, { "date": "March 18", "name": "F/x", "airing_time": "9:17 AM EDT", "details_spans": "['action|', 'R|', '1 hr 50 mins|', '1986|', 'HD|']" }, { "date": "March 18", "name": "Fx2: The Deadly Art of Illusion", "airing_time": "11:06 AM EDT", "details_spans": "['mystery & suspense|', 'PG-13|', '1 hr 49 mins|', '1991|', 'HD|']" }, { "date": "March 18", "name": "The Bourne Ultimatum", "airing_time": "12:55 PM EDT", "details_spans": "['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 18", "name": "The Frozen Ground", "airing_time": "2:51 PM EDT", "details_spans": "['drama|', '1 hr 46 mins|', '2013|']" }, { "date": "March 18", "name": "The Captive", "airing_time": "4:37 PM EDT", "details_spans": "['drama|', '16+|', '1 hr 53 mins|', '2014|', 'HD|']" }, { "date": "March 18", "name": "The Forbidden Kingdom", "airing_time": "6:30 PM EDT", "details_spans": "['action|', '1 hr 45 mins|', '2008|']" }, { "date": "March 18", "name": "Wonderland", "airing_time": "8:15 PM EDT", "details_spans": "['drama|', '1 hr 45 mins|', '2003|']" }, { "date": "March 18", "name": "Antitrust", "airing_time": "10:00 PM EDT", "details_spans": "['adventure|', 'PG-13|', '1 hr 49 mins|', '2001|', 'HD|']" }, { "date": "March 18", "name": "Green Room", "airing_time": "11:49 PM EDT", "details_spans": "['horror|', '1 hr 36 mins|', '2016|']" }, { "date": "March 18", "name": "Climax", "airing_time": "1:25 AM EDT", "details_spans": "['horror|', '1 hr 37 mins|', '2019|']" }, { "date": "March 18", "name": "Posse: The Revenge of Jessie Lee", "airing_time": "10:46 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 52 mins|', '1993|']" }, { "date": "March 18", "name": "The Adderall Diaries", "airing_time": "12:38 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 28 mins|', '2016|', 'HD|']" }, { "date": "March 18", "name": "The Company You Keep", "airing_time": "2:05 AM EDT", "details_spans": "['suspense|', 'NR|', '2 hrs 3 mins|', '2013|']" }, { "date": "March 18", "name": "Leatherheads", "airing_time": "4:07 AM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 55 mins|', '2016|', 'HD|']" }, { "date": "March 18", "name": "Hustle & Flow", "airing_time": "6:01 AM EDT", "details_spans": "['crime|', 'R|', '1 hr 59 mins|', '2005|', 'HD|']" }, { "date": "March 18", "name": "Poltergeist II: The Other Side", "airing_time": "7:58 AM EDT", "details_spans": "['horror|', 'NR|', '1 hr 33 mins|', '1986|']" }, { "date": "March 18", "name": "Paranoia", "airing_time": "9:29 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 48 mins|', '2013|', 'HD|']" }, { "date": "March 18", "name": "Leatherheads", "airing_time": "11:16 AM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 18", "name": "Straight Outta Compton", "airing_time": "1:10 PM EDT", "details_spans": "['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Philomena", "airing_time": "3:37 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']" }, { "date": "March 18", "name": "Whip It", "airing_time": "5:15 PM EDT", "details_spans": "['comedy|', '1 hr 51 mins|', '2009|']" }, { "date": "March 18", "name": "Another Country", "airing_time": "7:06 PM EDT", "details_spans": "['family relationships|', 'TV-MA|', '12 mins|', '2023|', 'HD|']" }, { "date": "March 18", "name": "I Love You, Beth Cooper", "airing_time": "7:18 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 18", "name": "I Now Pronounce You Chuck & Larry", "airing_time": "9:00 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 58 mins|', '2007|', 'HD|']" }, { "date": "March 18", "name": "Elizabethtown", "airing_time": "10:58 PM EDT", "details_spans": "['comedy|', '2 hrs 6 mins|', '2005|']" }, { "date": "March 18", "name": "Carrie", "airing_time": "1:04 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 18", "name": "The Spy Who Loved Me", "airing_time": "11:54 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 7 mins|', '1977|', 'HD|']" }, { "date": "March 18", "name": "Ex Machina", "airing_time": "2:00 AM EDT", "details_spans": "['artificial intelligence & robots|', 'R|', '1 hr 50 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Pride and Glory", "airing_time": "3:49 AM EDT", "details_spans": "['drama|', 'NR|', '2 hrs 11 mins|', '2008|']" }, { "date": "March 18", "name": "Bandits", "airing_time": "6:00 AM EDT", "details_spans": "['crime|', 'NR|', '2 hrs 3 mins|', '2001|']" }, { "date": "March 18", "name": "For Your Eyes Only", "airing_time": "8:03 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']" }, { "date": "March 18", "name": "The Pink Panther", "airing_time": "10:11 AM EDT", "details_spans": "['offbeat|', 'TV-PG|', '1 hr 57 mins|', '1964|', 'HD|']" }, { "date": "March 18", "name": "Chain Reaction", "airing_time": "12:07 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']" }, { "date": "March 18", "name": "Internal Affairs", "airing_time": "1:54 PM EDT", "details_spans": "['drama|', '1 hr 55 mins|', '1990|']" }, { "date": "March 18", "name": "Across the Universe", "airing_time": "3:49 PM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']" }, { "date": "March 18", "name": "The Soloist", "airing_time": "6:03 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 57 mins|', '2009|', 'HD|']" }, { "date": "March 18", "name": "Barely Lethal", "airing_time": "8:00 PM EDT", "details_spans": "['action|', '1 hr 39 mins|', '2015|']" }, { "date": "March 18", "name": "Scary Movie 2", "airing_time": "9:39 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']" }, { "date": "March 18", "name": "Scary Movie 3", "airing_time": "11:02 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']" }, { "date": "March 18", "name": "The Witch", "airing_time": "12:27 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 32 mins|', '2016|', 'HD|']" }, { "date": "March 18", "name": "Amy", "airing_time": "12:00 AM EDT", "details_spans": "['pop culture & celebrities|', 'R|', '2 hrs 9 mins|', '2015|', 'UHD|']" }, { "date": "March 18", "name": "Black Mass", "airing_time": "2:08 AM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 4 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Mass Ave", "airing_time": "4:11 AM EDT", "details_spans": "['drama|', 'TV-14|', '22 mins|', '2022|', 'HD|']" }, { "date": "March 18", "name": "Cujo", "airing_time": "4:32 AM EDT", "details_spans": "['cult horror|', 'R|', '1 hr 34 mins|', '1983|', 'HD|']" }, { "date": "March 18", "name": "Carrie", "airing_time": "6:05 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']" }, { "date": "March 18", "name": "Pet Sematary", "airing_time": "7:44 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 43 mins|', '1989|', 'HD|']" }, { "date": "March 18", "name": "The Second Best Exotic Marigold Hotel", "airing_time": "9:27 AM EDT", "details_spans": "['comedy|', 'NR|', '2 hrs 4 mins|', '2015|']" }, { "date": "March 18", "name": "Music Within", "airing_time": "11:30 AM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2007|']" }, { "date": "March 18", "name": "Nothing Like the Holidays", "airing_time": "1:04 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 18", "name": "Equals", "airing_time": "2:43 PM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2016|']" }, { "date": "March 18", "name": "Office Space", "airing_time": "4:26 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 18", "name": "House at the End of the Street", "airing_time": "5:56 PM EDT", "details_spans": "['horror|', '1 hr 41 mins|', '2012|']" }, { "date": "March 18", "name": "Life After Beth", "airing_time": "7:37 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']" }, { "date": "March 18", "name": "Slice", "airing_time": "9:06 PM EDT", "details_spans": "['comedy|', '1 hr 23 mins|', '2018|']" }, { "date": "March 18", "name": "Cyrus", "airing_time": "10:29 PM EDT", "details_spans": "['romance|', '1 hr 31 mins|', '2010|']" }, { "date": "March 18", "name": "Fifty Shades of Grey", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Taxi", "airing_time": "11:24 PM EDT", "details_spans": "['action|', 'NR|', '1 hr 38 mins|', '2004|']" }, { "date": "March 18", "name": "Pieces of April", "airing_time": "1:02 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 21 mins|', '2003|']" }, { "date": "March 18", "name": "Never Goin' Back", "airing_time": "2:22 AM EDT", "details_spans": "['comedy|', 'NR|', '1 hr 28 mins|', '2018|']" }, { "date": "March 18", "name": "Life After Beth", "airing_time": "3:49 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 30 mins|', '2014|', 'HD|']" }, { "date": "March 18", "name": "Fast Color", "airing_time": "5:18 AM EDT", "details_spans": "['drama|', 'NR|', '1 hr 43 mins|', '2019|']" }, { "date": "March 18", "name": "Fifty Shades Darker", "airing_time": "7:00 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 59 mins|', '2017|', 'HD|']" }, { "date": "March 18", "name": "Rescue Dawn", "airing_time": "8:58 AM EDT", "details_spans": "['action|', 'NR|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 18", "name": "Election", "airing_time": "11:04 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']" }, { "date": "March 18", "name": "Don't Let Go", "airing_time": "12:47 PM EDT", "details_spans": "['horror|', '1 hr 43 mins|', '2019|']" }, { "date": "March 18", "name": "Barbershop", "airing_time": "2:30 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 18", "name": "Yes Man", "airing_time": "4:13 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 18", "name": "The Company You Keep", "airing_time": "5:58 PM EDT", "details_spans": "['suspense|', '2 hrs 2 mins|', '2013|']" }, { "date": "March 18", "name": "Unbreakable", "airing_time": "8:00 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 18", "name": "Kingsman: The Secret Service", "airing_time": "9:47 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 18", "name": "Blue Valentine", "airing_time": "11:56 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 19", "name": "Blue Valentine", "airing_time": "11:56 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 19", "name": "A Glimpse Inside the Mind of Charles Swan III", "airing_time": "1:49 AM EDT", "details_spans": "['comedy|', '1 hr 26 mins|', '2013|']" }, { "date": "March 19", "name": "The Rover", "airing_time": "3:15 AM EDT", "details_spans": "['action|', 'R|', '1 hr 43 mins|', '2014|', 'HD|']" }, { "date": "March 19", "name": "Midnight Sun", "airing_time": "4:58 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']" }, { "date": "March 19", "name": "Buffy the Vampire Slayer", "airing_time": "6:30 AM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']" }, { "date": "March 19", "name": "Paparazzi", "airing_time": "7:56 AM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2004|']" }, { "date": "March 19", "name": "Paranoia", "airing_time": "9:21 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Let Me In", "airing_time": "11:08 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 19", "name": "Barbershop 2: Back in Business", "airing_time": "1:04 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']" }, { "date": "March 19", "name": "Welcome Home Roscoe Jenkins", "airing_time": "2:51 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']" }, { "date": "March 19", "name": "House at the End of the Street", "airing_time": "4:45 PM EDT", "details_spans": "['horror|', '1 hr 41 mins|', '2012|']" }, { "date": "March 19", "name": "Poltergeist (2015)", "airing_time": "6:26 PM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']" }, { "date": "March 19", "name": "Predators", "airing_time": "8:00 PM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']" }, { "date": "March 19", "name": "Attack the Block", "airing_time": "9:47 PM EDT", "details_spans": "['action|', '1 hr 29 mins|', '2011|']" }, { "date": "March 19", "name": "Black Mass", "airing_time": "11:16 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "American Heist", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '2015|']" }, { "date": "March 19", "name": "Dumb and Dumber To", "airing_time": "10:36 PM EDT", "details_spans": "['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']" }, { "date": "March 19", "name": "Hot Summer Nights", "airing_time": "12:26 AM EDT", "details_spans": "['comedy|', '1 hr 49 mins|', '2018|']" }, { "date": "March 19", "name": "Kingpin", "airing_time": "2:15 AM EDT", "details_spans": "['comedy|', '1 hr 54 mins|', '1996|']" }, { "date": "March 19", "name": "Mermaids", "airing_time": "4:09 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 51 mins|', '1990|', 'HD|']" }, { "date": "March 19", "name": "The Last Movie Star", "airing_time": "6:00 AM EDT", "details_spans": "['drama|', '1 hr 44 mins|', '2018|']" }, { "date": "March 19", "name": "Walk the Line", "airing_time": "7:44 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '2 hrs 17 mins|', '2005|', 'HD|']" }, { "date": "March 19", "name": "Across the Universe", "airing_time": "10:01 AM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']" }, { "date": "March 19", "name": "Ingrid Goes West", "airing_time": "12:15 PM EDT", "details_spans": "['comedy|', '1 hr 38 mins|', '2017|']" }, { "date": "March 19", "name": "Mojave", "airing_time": "1:53 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2016|']" }, { "date": "March 19", "name": "Dark Places", "airing_time": "3:27 PM EDT", "details_spans": "['drama|', '1 hr 54 mins|', '2015|']" }, { "date": "March 19", "name": "Appaloosa", "airing_time": "5:21 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']" }, { "date": "March 19", "name": "Jennifer's Body", "airing_time": "7:17 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']" }, { "date": "March 19", "name": "The Visit", "airing_time": "9:00 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 34 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "Gemini", "airing_time": "10:34 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2018|']" }, { "date": "March 19", "name": "Ronin", "airing_time": "12:08 AM EDT", "details_spans": "['action|', '2 hrs 2 mins|', '1998|']" }, { "date": "March 19", "name": "Don't Let Go", "airing_time": "11:29 PM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 19", "name": "Extortion", "airing_time": "1:13 AM EDT", "details_spans": "['action|', '1 hr 50 mins|', '2017|']" }, { "date": "March 19", "name": "Buffy the Vampire Slayer", "airing_time": "3:03 AM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 27 mins|', '1992|', 'HD|']" }, { "date": "March 19", "name": "Wes Craven Presents: They", "airing_time": "4:30 AM EDT", "details_spans": "['horror|', '1 hr 30 mins|', '2002|']" }, { "date": "March 19", "name": "Poltergeist II: The Other Side", "airing_time": "6:00 AM EDT", "details_spans": "['horror|', '1 hr 31 mins|', '1986|']" }, { "date": "March 19", "name": "Poltergeist III", "airing_time": "7:31 AM EDT", "details_spans": "['horror|', '1 hr 38 mins|', '1988|']" }, { "date": "March 19", "name": "Another Country", "airing_time": "9:09 AM EDT", "details_spans": "['family relationships|', 'TV-MA|', '13 mins|', '2023|', 'HD|']" }, { "date": "March 19", "name": "Line of Duty", "airing_time": "9:22 AM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2013|']" }, { "date": "March 19", "name": "Vacation", "airing_time": "11:03 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 39 mins|', '2015|', 'UHD|']" }, { "date": "March 19", "name": "Kingsman: The Secret Service", "airing_time": "12:42 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "Paycheck", "airing_time": "2:51 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']" }, { "date": "March 19", "name": "Taxi", "airing_time": "4:50 PM EDT", "details_spans": "['action|', '1 hr 38 mins|', '2004|']" }, { "date": "March 19", "name": "XXX", "airing_time": "6:28 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']" }, { "date": "March 19", "name": "Observe and Report", "airing_time": "8:33 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']" }, { "date": "March 19", "name": "Posse: The Revenge of Jessie Lee", "airing_time": "10:00 PM EDT", "details_spans": "['action|', '1 hr 52 mins|', '1993|']" }, { "date": "March 19", "name": "Robin Hood (1991)", "airing_time": "11:52 PM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 45 mins|', '1991|']" }, { "date": "March 19", "name": "Green Room", "airing_time": "11:49 PM EDT", "details_spans": "['horror|', '1 hr 36 mins|', '2016|']" }, { "date": "March 19", "name": "Climax", "airing_time": "1:25 AM EDT", "details_spans": "['horror|', '1 hr 37 mins|', '2019|']" }, { "date": "March 19", "name": "Hotel Mumbai", "airing_time": "3:02 AM EDT", "details_spans": "['action|', '2 hrs 4 mins|', '2019|']" }, { "date": "March 19", "name": "Into the Blue", "airing_time": "5:06 AM EDT", "details_spans": "['action|', '1 hr 51 mins|', '2005|']" }, { "date": "March 19", "name": "The Fury", "airing_time": "6:57 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 59 mins|', '1978|', 'HD|']" }, { "date": "March 19", "name": "Taken 3", "airing_time": "8:56 AM EDT", "details_spans": "['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "A Most Violent Year", "airing_time": "10:45 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "The Thomas Crown Affair", "airing_time": "12:50 PM EDT", "details_spans": "['romance|', '1 hr 54 mins|', '1999|']" }, { "date": "March 19", "name": "Blown Away", "airing_time": "2:44 PM EDT", "details_spans": "['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']" }, { "date": "March 19", "name": "Friday the 13th", "airing_time": "4:45 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']" }, { "date": "March 19", "name": "Friday the 13th, Part II", "airing_time": "6:21 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 27 mins|', '1981|', 'HD|']" }, { "date": "March 19", "name": "A View to a Kill", "airing_time": "7:48 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']" }, { "date": "March 19", "name": "The Witch", "airing_time": "10:00 PM EDT", "details_spans": "['occult|', 'R|', '1 hr 33 mins|', '2016|', 'HD|']" }, { "date": "March 19", "name": "The Strangers: Prey at Night", "airing_time": "11:33 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']" }, { "date": "March 19", "name": "Angel of Mine", "airing_time": "12:59 AM EDT", "details_spans": "['drama|', '1 hr 39 mins|', '2019|']" }, { "date": "March 19", "name": "Elizabethtown", "airing_time": "10:58 PM EDT", "details_spans": "['comedy|', '2 hrs 6 mins|', '2005|']" }, { "date": "March 19", "name": "Carrie", "airing_time": "1:04 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Winter's Tale", "airing_time": "2:44 AM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 58 mins|', '2014|']" }, { "date": "March 19", "name": "Teen Wolf", "airing_time": "4:42 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 19", "name": "Talk Black", "airing_time": "6:15 AM EDT", "details_spans": "['comedy|', 'TV-14|', '13 mins|', '2023|', 'HD|']" }, { "date": "March 19", "name": "Mud", "airing_time": "6:28 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Missing in Action 2 - The Beginning", "airing_time": "8:39 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '1985|']" }, { "date": "March 19", "name": "Election", "airing_time": "10:15 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']" }, { "date": "March 19", "name": "Johnny Dangerously", "airing_time": "11:58 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']" }, { "date": "March 19", "name": "John Dies at the End", "airing_time": "1:29 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Baby Mama", "airing_time": "3:09 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 19", "name": "Killing Me Softly", "airing_time": "4:48 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 40 mins|', '2003|', 'HD|']" }, { "date": "March 19", "name": "Mass Ave", "airing_time": "6:28 PM EDT", "details_spans": "['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']" }, { "date": "March 19", "name": "Pure", "airing_time": "6:49 PM EDT", "details_spans": "['romantic dramas|', 'TV-14|', '13 mins|', '2022|', 'HD|']" }, { "date": "March 19", "name": "Winter's Tale", "airing_time": "7:02 PM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 58 mins|', '2014|']" }, { "date": "March 19", "name": "Beauty Shop", "airing_time": "9:00 PM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']" }, { "date": "March 19", "name": "The Second Best Exotic Marigold Hotel", "airing_time": "10:46 PM EDT", "details_spans": "['comedy|', '2 hrs 3 mins|', '2015|']" }, { "date": "March 19", "name": "Fifty Shades Darker", "airing_time": "12:49 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 19", "name": "Scary Movie 3", "airing_time": "11:02 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']" }, { "date": "March 19", "name": "The Witch", "airing_time": "12:27 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 32 mins|', '2016|', 'HD|']" }, { "date": "March 19", "name": "Shining Through", "airing_time": "1:59 AM EDT", "details_spans": "['romance|', '2 hrs 13 mins|', '1992|']" }, { "date": "March 19", "name": "Leap of Faith", "airing_time": "4:12 AM EDT", "details_spans": "['comedy|', '1 hr 48 mins|', '1992|']" }, { "date": "March 19", "name": "The Return of the Pink Panther", "airing_time": "6:00 AM EDT", "details_spans": "['big screen crime|', 'G|', '1 hr 53 mins|', '1975|', 'HD|']" }, { "date": "March 19", "name": "Never Say Never Again", "airing_time": "7:53 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 15 mins|', '1983|', 'HD|']" }, { "date": "March 19", "name": "The Pink Panther Strikes Again", "airing_time": "10:08 AM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']" }, { "date": "March 19", "name": "Spaceballs", "airing_time": "11:52 AM EDT", "details_spans": "['fantasy & sci-fi|', 'PG|', '1 hr 37 mins|', '1987|', 'HD|']" }, { "date": "March 19", "name": "Shiva Baby", "airing_time": "1:29 PM EDT", "details_spans": "['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']" }, { "date": "March 19", "name": "Wonderland", "airing_time": "2:47 PM EDT", "details_spans": "['drama|', '1 hr 44 mins|', '2003|']" }, { "date": "March 19", "name": "Locke", "airing_time": "4:31 PM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2014|']" }, { "date": "March 19", "name": "Youth", "airing_time": "5:56 PM EDT", "details_spans": "['drama|', '2 hrs 4 mins|', '2015|']" }, { "date": "March 19", "name": "The Show", "airing_time": "8:00 PM EDT", "details_spans": "['drama|', '1 hr 45 mins|', '2017|']" }, { "date": "March 19", "name": "Death Wish 3", "airing_time": "9:45 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 31 mins|', '1985|', 'HD|']" }, { "date": "March 19", "name": "Revenge of the Green Dragons", "airing_time": "11:16 PM EDT", "details_spans": "['action|', '1 hr 35 mins|', '2014|']" }, { "date": "March 19", "name": "Little Woods", "airing_time": "12:51 AM EDT", "details_spans": "['suspense|', '1 hr 44 mins|', '2019|']" }, { "date": "March 19", "name": "Fifty Shades of Grey", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "Mud", "airing_time": "2:06 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Neh", "airing_time": "4:17 AM EDT", "details_spans": "['romantic dramas|', 'TV-14|', '15 mins|', '2021|', 'HD|']" }, { "date": "March 19", "name": "Morris From America", "airing_time": "4:32 AM EDT", "details_spans": "['comedy|', '1 hr 31 mins|', '2016|']" }, { "date": "March 19", "name": "Pieces of April", "airing_time": "6:03 AM EDT", "details_spans": "['comedy|', '1 hr 20 mins|', '2003|']" }, { "date": "March 19", "name": "Pulling Strings", "airing_time": "7:23 AM EDT", "details_spans": "['romantic comedy|', 'PG|', '1 hr 52 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Obvious Child", "airing_time": "9:15 AM EDT", "details_spans": "['comedy|', '1 hr 25 mins|', '2014|']" }, { "date": "March 19", "name": "Laggies", "airing_time": "10:40 AM EDT", "details_spans": "['comedy|', '1 hr 40 mins|', '2014|']" }, { "date": "March 19", "name": "The Sentinel", "airing_time": "12:20 PM EDT", "details_spans": "['action|', '1 hr 49 mins|', '2006|']" }, { "date": "March 19", "name": "Pet Sematary Two", "airing_time": "2:09 PM EDT", "details_spans": "['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']" }, { "date": "March 19", "name": "Slums of Beverly Hills", "airing_time": "3:50 PM EDT", "details_spans": "['comedy|', '1 hr 32 mins|', '1998|']" }, { "date": "March 19", "name": "Wes Craven Presents: They", "airing_time": "5:22 PM EDT", "details_spans": "['horror|', '1 hr 30 mins|', '2002|']" }, { "date": "March 19", "name": "Rescue Dawn", "airing_time": "6:52 PM EDT", "details_spans": "['action|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 19", "name": "She's Out of My League", "airing_time": "8:58 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']" }, { "date": "March 19", "name": "The B-Side: Elsa Dorfman's Portrait Photography", "airing_time": "10:43 PM EDT", "details_spans": "['biographies|', 'R|', '1 hr 17 mins|', '2017|', 'HD|']" }, { "date": "March 19", "name": "The Vanishing of Sidney Hall", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', '2 hrs 1 min|', '2018|']" }, { "date": "March 19", "name": "Blue Valentine", "airing_time": "11:56 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 19", "name": "A Glimpse Inside the Mind of Charles Swan III", "airing_time": "1:49 AM EDT", "details_spans": "['comedy|', '1 hr 26 mins|', '2013|']" }, { "date": "March 19", "name": "Robin Hood (1991)", "airing_time": "3:15 AM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 43 mins|', '1991|']" }, { "date": "March 19", "name": "Midnight Sun", "airing_time": "4:58 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']" }, { "date": "March 19", "name": "Buffy the Vampire Slayer", "airing_time": "6:30 AM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']" }, { "date": "March 19", "name": "Paparazzi", "airing_time": "7:56 AM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2004|']" }, { "date": "March 19", "name": "Paranoia", "airing_time": "9:21 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']" }, { "date": "March 19", "name": "Let Me In", "airing_time": "11:08 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 19", "name": "Barbershop 2: Back in Business", "airing_time": "1:04 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2004|', 'HD|']" }, { "date": "March 19", "name": "Welcome Home Roscoe Jenkins", "airing_time": "2:51 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']" }, { "date": "March 19", "name": "House at the End of the Street", "airing_time": "4:45 PM EDT", "details_spans": "['horror|', '1 hr 41 mins|', '2012|']" }, { "date": "March 19", "name": "Poltergeist (2015)", "airing_time": "6:26 PM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']" }, { "date": "March 19", "name": "Predators", "airing_time": "8:00 PM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']" }, { "date": "March 19", "name": "Attack the Block", "airing_time": "9:47 PM EDT", "details_spans": "['action|', '1 hr 29 mins|', '2011|']" }, { "date": "March 19", "name": "Black Mass", "airing_time": "11:16 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 19", "name": "American Heist", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '2015|']" }, { "date": "March 20", "name": "Black Mass", "airing_time": "11:16 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 20", "name": "American Heist", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '2015|']" }, { "date": "March 20", "name": "Richard III", "airing_time": "2:55 AM EDT", "details_spans": "['classics|', 'R|', '1 hr 45 mins|', '1995|', 'HD|']" }, { "date": "March 20", "name": "Pawn Sacrifice", "airing_time": "4:40 AM EDT", "details_spans": "['drama|', '1 hr 56 mins|', '2015|']" }, { "date": "March 20", "name": "Mud", "airing_time": "6:36 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 20", "name": "Luce", "airing_time": "8:47 AM EDT", "details_spans": "['drama|', '1 hr 50 mins|', '2019|']" }, { "date": "March 20", "name": "Elizabethtown", "airing_time": "10:37 AM EDT", "details_spans": "['comedy|', '2 hrs 4 mins|', '2005|']" }, { "date": "March 20", "name": "Hustle & Flow", "airing_time": "12:41 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']" }, { "date": "March 20", "name": "The Promise", "airing_time": "2:38 PM EDT", "details_spans": "['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']" }, { "date": "March 20", "name": "Office Space", "airing_time": "4:51 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 20", "name": "Baby Mama", "airing_time": "6:21 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 20", "name": "Life After Beth", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']" }, { "date": "March 20", "name": "The Dead Don't Die", "airing_time": "9:29 PM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 20", "name": "Carrie", "airing_time": "11:14 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 20", "name": "I Love You, Beth Cooper", "airing_time": "12:54 AM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 20", "name": "Gemini", "airing_time": "10:34 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2018|']" }, { "date": "March 20", "name": "Ronin", "airing_time": "12:08 AM EDT", "details_spans": "['action|', '2 hrs 2 mins|', '1998|']" }, { "date": "March 20", "name": "Altitude", "airing_time": "2:10 AM EDT", "details_spans": "['action|', '1 hr 28 mins|', '2017|']" }, { "date": "March 20", "name": "On Her Majesty's Secret Service", "airing_time": "3:38 AM EDT", "details_spans": "['adventure|', 'PG|', '2 hrs 23 mins|', '1969|', 'HD|']" }, { "date": "March 20", "name": "Wild Rose", "airing_time": "6:01 AM EDT", "details_spans": "['drama|', '1 hr 41 mins|', '2019|']" }, { "date": "March 20", "name": "Temptation: Confessions of a Marriage Counselor", "airing_time": "7:42 AM EDT", "details_spans": "['drama|', '1 hr 52 mins|', '2013|']" }, { "date": "March 20", "name": "Genius", "airing_time": "9:34 AM EDT", "details_spans": "['drama|', '1 hr 45 mins|', '2016|']" }, { "date": "March 20", "name": "A Serious Man", "airing_time": "11:19 AM EDT", "details_spans": "['comedy|', '1 hr 46 mins|', '2009|']" }, { "date": "March 20", "name": "Scary Movie", "airing_time": "1:05 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 29 mins|', '2000|', 'HD|']" }, { "date": "March 20", "name": "Scary Movie 2", "airing_time": "2:34 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']" }, { "date": "March 20", "name": "Scary Movie 3", "airing_time": "3:57 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']" }, { "date": "March 20", "name": "Chain Reaction", "airing_time": "5:22 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']" }, { "date": "March 20", "name": "Into the Blue", "airing_time": "7:09 PM EDT", "details_spans": "['action|', '1 hr 51 mins|', '2005|']" }, { "date": "March 20", "name": "Into the Blue 2: The Reef", "airing_time": "9:00 PM EDT", "details_spans": "['suspense|', 'TV-MA|', '1 hr 32 mins|', '2009|']" }, { "date": "March 20", "name": "The New World", "airing_time": "10:32 PM EDT", "details_spans": "['drama|', '2 hrs 16 mins|', '2005|']" }, { "date": "March 20", "name": "Bandits", "airing_time": "12:48 AM EDT", "details_spans": "['crime|', '2 hrs 3 mins|', '2001|']" }, { "date": "March 20", "name": "Robin Hood (1991)", "airing_time": "11:52 PM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 45 mins|', '1991|']" }, { "date": "March 20", "name": "Hotel Artemis", "airing_time": "1:37 AM EDT", "details_spans": "['action|', '1 hr 34 mins|', '2018|']" }, { "date": "March 20", "name": "Children of the Corn: The Gathering", "airing_time": "3:11 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 26 mins|', '1996|', 'HD|']" }, { "date": "March 20", "name": "Slice", "airing_time": "4:37 AM EDT", "details_spans": "['comedy|', '1 hr 23 mins|', '2018|']" }, { "date": "March 20", "name": "Missing in Action", "airing_time": "6:00 AM EDT", "details_spans": "['action|', '1 hr 42 mins|', '1984|']" }, { "date": "March 20", "name": "Teen Wolf", "airing_time": "7:42 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 20", "name": "Yes Man", "airing_time": "9:15 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 20", "name": "Whip It", "airing_time": "11:00 AM EDT", "details_spans": "['comedy|', '1 hr 51 mins|', '2009|']" }, { "date": "March 20", "name": "Killing Me Softly", "airing_time": "12:51 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 40 mins|', '2003|', 'HD|']" }, { "date": "March 20", "name": "Beauty Shop", "airing_time": "2:31 PM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']" }, { "date": "March 20", "name": "Barbershop", "airing_time": "4:17 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 20", "name": "Poltergeist (2015)", "airing_time": "6:00 PM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']" }, { "date": "March 20", "name": "Strange Days", "airing_time": "7:34 PM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 20", "name": "The Sentinel", "airing_time": "10:00 PM EDT", "details_spans": "['action|', '1 hr 48 mins|', '2006|']" }, { "date": "March 20", "name": "I Am Wrath", "airing_time": "11:48 PM EDT", "details_spans": "['action|', '1 hr 31 mins|', '2016|']" }, { "date": "March 20", "name": "Rescue Dawn", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 20", "name": "The Strangers: Prey at Night", "airing_time": "11:33 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']" }, { "date": "March 20", "name": "Angel of Mine", "airing_time": "12:59 AM EDT", "details_spans": "['drama|', '1 hr 39 mins|', '2019|']" }, { "date": "March 20", "name": "Ghost", "airing_time": "2:38 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']" }, { "date": "March 20", "name": "Live and Let Die", "airing_time": "4:45 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 2 mins|', '1973|', 'HD|']" }, { "date": "March 20", "name": "Skin Can Breathe", "airing_time": "6:47 AM EDT", "details_spans": "['drama|', 'TV-14|', '12 mins|', '2022|', 'HD|']" }, { "date": "March 20", "name": "Internal Affairs", "airing_time": "6:59 AM EDT", "details_spans": "['drama|', '1 hr 55 mins|', '1990|']" }, { "date": "March 20", "name": "The Gift", "airing_time": "8:54 AM EDT", "details_spans": "['drama|', 'TV-14|', '15 mins|', '2022|', 'HD|']" }, { "date": "March 20", "name": "Victor Frankenstein", "airing_time": "9:09 AM EDT", "details_spans": "['horror|', '1 hr 50 mins|', '2015|']" }, { "date": "March 20", "name": "For Your Eyes Only", "airing_time": "10:59 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 9 mins|', '1981|', 'HD|']" }, { "date": "March 20", "name": "Body of Lies", "airing_time": "1:08 PM EDT", "details_spans": "['action|', '2 hrs 9 mins|', '2008|']" }, { "date": "March 20", "name": "Fargo", "airing_time": "3:17 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 39 mins|', '1996|', 'HD|']" }, { "date": "March 20", "name": "Friday the 13th Part III", "airing_time": "4:56 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 36 mins|', '1982|', 'HD|']" }, { "date": "March 20", "name": "Friday the 13th-The Final Chapter", "airing_time": "6:32 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 32 mins|', '1984|', 'HD|']" }, { "date": "March 20", "name": "The Pink Panther", "airing_time": "8:04 PM EDT", "details_spans": "['offbeat|', 'TV-PG|', '1 hr 56 mins|', '1964|', 'HD|']" }, { "date": "March 20", "name": "A Shot in the Dark", "airing_time": "10:00 PM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 43 mins|', '1964|', 'HD|']" }, { "date": "March 20", "name": "The Pink Panther Strikes Again", "airing_time": "11:43 PM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']" }, { "date": "March 20", "name": "Revenge of the Pink Panther", "airing_time": "1:27 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']" }, { "date": "March 20", "name": "The Second Best Exotic Marigold Hotel", "airing_time": "10:46 PM EDT", "details_spans": "['comedy|', '2 hrs 3 mins|', '2015|']" }, { "date": "March 20", "name": "Fifty Shades Darker", "airing_time": "12:49 AM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 20", "name": "Fifty Shades Freed", "airing_time": "2:47 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']" }, { "date": "March 20", "name": "The Dead Don't Die", "airing_time": "4:33 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 20", "name": "Carrie", "airing_time": "6:18 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']" }, { "date": "March 20", "name": "How to Talk to Girls at Parties", "airing_time": "7:57 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']" }, { "date": "March 20", "name": "Major League", "airing_time": "9:40 AM EDT", "details_spans": "['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']" }, { "date": "March 20", "name": "XXX: State of the Union", "airing_time": "11:27 AM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 20", "name": "The War of the Roses", "airing_time": "1:08 PM EDT", "details_spans": "['comedy|', '1 hr 57 mins|', '1989|']" }, { "date": "March 20", "name": "Equals", "airing_time": "3:05 PM EDT", "details_spans": "['drama|', '1 hr 44 mins|', '2016|']" }, { "date": "March 20", "name": "Talk Black", "airing_time": "4:49 PM EDT", "details_spans": "['comedy|', 'TV-14|', '14 mins|', '2023|', 'HD|']" }, { "date": "March 20", "name": "The Edge", "airing_time": "5:03 PM EDT", "details_spans": "['suspense|', '1 hr 58 mins|', '1997|']" }, { "date": "March 20", "name": "Paycheck", "airing_time": "7:01 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']" }, { "date": "March 20", "name": "Kingsman: The Secret Service", "airing_time": "9:00 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 20", "name": "Blue Valentine", "airing_time": "11:09 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 20", "name": "Don't Let Go", "airing_time": "1:02 AM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 20", "name": "Revenge of the Green Dragons", "airing_time": "11:16 PM EDT", "details_spans": "['action|', '1 hr 35 mins|', '2014|']" }, { "date": "March 20", "name": "Little Woods", "airing_time": "12:51 AM EDT", "details_spans": "['suspense|', '1 hr 44 mins|', '2019|']" }, { "date": "March 20", "name": "Hot Summer Nights", "airing_time": "2:35 AM EDT", "details_spans": "['comedy|', '1 hr 47 mins|', '2018|']" }, { "date": "March 20", "name": "Friday the 13th", "airing_time": "4:22 AM EDT", "details_spans": "['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']" }, { "date": "March 20", "name": "Friday the 13th", "airing_time": "6:00 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 35 mins|', '1980|', 'HD|']" }, { "date": "March 20", "name": "Leap of Faith", "airing_time": "7:35 AM EDT", "details_spans": "['comedy|', '1 hr 48 mins|', '1992|']" }, { "date": "March 20", "name": "Mermaids", "airing_time": "9:23 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 51 mins|', '1990|', 'HD|']" }, { "date": "March 20", "name": "Friday the 13th Part VIII: Jason Takes Manhattan", "airing_time": "11:14 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 41 mins|', '1989|', 'HD|']" }, { "date": "March 20", "name": "Kingpin", "airing_time": "12:55 PM EDT", "details_spans": "['comedy|', '1 hr 54 mins|', '1996|']" }, { "date": "March 20", "name": "Appaloosa", "airing_time": "2:49 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']" }, { "date": "March 20", "name": "Whiteout", "airing_time": "4:45 PM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2009|']" }, { "date": "March 20", "name": "The Importance of Being Earnest", "airing_time": "6:26 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2002|']" }, { "date": "March 20", "name": "De-Lovely", "airing_time": "8:00 PM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 6 mins|', '2004|', 'HD|']" }, { "date": "March 20", "name": "The Captive", "airing_time": "10:06 PM EDT", "details_spans": "['drama|', '16+|', '1 hr 52 mins|', '2014|', 'HD|']" }, { "date": "March 20", "name": "Hotel Mumbai", "airing_time": "11:58 PM EDT", "details_spans": "['action|', '2 hrs 4 mins|', '2019|']" }, { "date": "March 20", "name": "The Vanishing of Sidney Hall", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', '2 hrs 1 min|', '2018|']" }, { "date": "March 20", "name": "Come and Find Me", "airing_time": "2:01 AM EDT", "details_spans": "['drama|', '1 hr 53 mins|', '2016|']" }, { "date": "March 20", "name": "Fast Color", "airing_time": "3:54 AM EDT", "details_spans": "['drama|', '1 hr 42 mins|', '2019|']" }, { "date": "March 20", "name": "Cracked", "airing_time": "5:36 AM EDT", "details_spans": "['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']" }, { "date": "March 20", "name": "Pens & Pencils", "airing_time": "5:52 AM EDT", "details_spans": "['mystery & suspense|', 'TV-14|', '16 mins|', '2023|', 'HD|']" }, { "date": "March 20", "name": "Teen Wolf Too", "airing_time": "6:08 AM EDT", "details_spans": "['cult classic|', 'PG|', '1 hr 35 mins|', '1987|', 'HD|']" }, { "date": "March 20", "name": "Unbreakable", "airing_time": "7:43 AM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 48 mins|', '2000|', 'HD|']" }, { "date": "March 20", "name": "A Night at the Roxbury", "airing_time": "9:31 AM EDT", "details_spans": "['offbeat|', 'PG-13|', '1 hr 23 mins|', '1998|', 'HD|']" }, { "date": "March 20", "name": "Backstabbing for Beginners", "airing_time": "10:54 AM EDT", "details_spans": "['suspense|', '1 hr 49 mins|', '2018|']" }, { "date": "March 20", "name": "Master of the Shadowless Kick: Wong Kei-Ying", "airing_time": "12:43 PM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 39 mins|', '2017|']" }, { "date": "March 20", "name": "Master of the Drunken Fist: Beggar So", "airing_time": "2:22 PM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 39 mins|', '2017|']" }, { "date": "March 20", "name": "Buffy the Vampire Slayer", "airing_time": "4:01 PM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']" }, { "date": "March 20", "name": "Fifty Shades Freed", "airing_time": "5:27 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']" }, { "date": "March 20", "name": "The Goods: Live Hard, Sell Hard", "airing_time": "7:13 PM EDT", "details_spans": "['comedy|', '1 hr 30 mins|', '2009|']" }, { "date": "March 20", "name": "John Dies at the End", "airing_time": "8:43 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 20", "name": "Climax", "airing_time": "10:23 PM EDT", "details_spans": "['horror|', '1 hr 37 mins|', '2019|']" }, { "date": "March 20", "name": "A Prayer Before Dawn", "airing_time": "12:00 AM EDT", "details_spans": "['action|', '1 hr 58 mins|', '2018|']" }, { "date": "March 20", "name": "Black Mass", "airing_time": "11:16 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 20", "name": "American Heist", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '2015|']" }, { "date": "March 20", "name": "Richard III", "airing_time": "2:55 AM EDT", "details_spans": "['classics|', 'R|', '1 hr 45 mins|', '1995|', 'HD|']" }, { "date": "March 20", "name": "Pawn Sacrifice", "airing_time": "4:40 AM EDT", "details_spans": "['drama|', '1 hr 56 mins|', '2015|']" }, { "date": "March 20", "name": "Mud", "airing_time": "6:36 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 20", "name": "Luce", "airing_time": "8:47 AM EDT", "details_spans": "['drama|', '1 hr 50 mins|', '2019|']" }, { "date": "March 20", "name": "Elizabethtown", "airing_time": "10:37 AM EDT", "details_spans": "['comedy|', '2 hrs 4 mins|', '2005|']" }, { "date": "March 20", "name": "Hustle & Flow", "airing_time": "12:41 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']" }, { "date": "March 20", "name": "The Promise", "airing_time": "2:38 PM EDT", "details_spans": "['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']" }, { "date": "March 20", "name": "Office Space", "airing_time": "4:51 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 20", "name": "Baby Mama", "airing_time": "6:21 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 20", "name": "Life After Beth", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']" }, { "date": "March 20", "name": "The Dead Don't Die", "airing_time": "9:29 PM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 20", "name": "Carrie", "airing_time": "11:14 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 20", "name": "I Love You, Beth Cooper", "airing_time": "12:54 AM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 21", "name": "Carrie", "airing_time": "11:14 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 21", "name": "I Love You, Beth Cooper", "airing_time": "12:54 AM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 21", "name": "Amy", "airing_time": "2:36 AM EDT", "details_spans": "['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']" }, { "date": "March 21", "name": "Dim Sum Funeral", "airing_time": "4:44 AM EDT", "details_spans": "['drama|', '1 hr 37 mins|', '2009|']" }, { "date": "March 21", "name": "Pieces of April", "airing_time": "6:21 AM EDT", "details_spans": "['comedy|', '1 hr 21 mins|', '2003|']" }, { "date": "March 21", "name": "Paparazzi", "airing_time": "7:42 AM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2004|']" }, { "date": "March 21", "name": "Buffy the Vampire Slayer", "airing_time": "9:07 AM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']" }, { "date": "March 21", "name": "Barbershop", "airing_time": "10:33 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 21", "name": "XXX: State of the Union", "airing_time": "12:16 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 21", "name": "Kingsman: The Secret Service", "airing_time": "1:57 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Fifty Shades Darker", "airing_time": "4:06 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 21", "name": "I Now Pronounce You Chuck & Larry", "airing_time": "6:04 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "Election", "airing_time": "8:00 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']" }, { "date": "March 21", "name": "Shiva Baby", "airing_time": "9:43 PM EDT", "details_spans": "['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']" }, { "date": "March 21", "name": "Unbreakable", "airing_time": "11:01 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 21", "name": "Fast Color", "airing_time": "12:48 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2019|']" }, { "date": "March 21", "name": "The New World", "airing_time": "10:32 PM EDT", "details_spans": "['drama|', '2 hrs 16 mins|', '2005|']" }, { "date": "March 21", "name": "Bandits", "airing_time": "12:48 AM EDT", "details_spans": "['crime|', '2 hrs 3 mins|', '2001|']" }, { "date": "March 21", "name": "Charlie Wilson's War", "airing_time": "2:51 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 42 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "McQueen", "airing_time": "4:33 AM EDT", "details_spans": "['documentary|', '1 hr 52 mins|', '2018|']" }, { "date": "March 21", "name": "Blown Away", "airing_time": "6:25 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']" }, { "date": "March 21", "name": "Youth", "airing_time": "8:26 AM EDT", "details_spans": "['drama|', '2 hrs 4 mins|', '2015|']" }, { "date": "March 21", "name": "Paper Towns", "airing_time": "10:30 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 50 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "The Thomas Crown Affair", "airing_time": "12:20 PM EDT", "details_spans": "['romance|', '1 hr 54 mins|', '1999|']" }, { "date": "March 21", "name": "Little Woods", "airing_time": "2:14 PM EDT", "details_spans": "['suspense|', '1 hr 44 mins|', '2019|']" }, { "date": "March 21", "name": "Wonderland", "airing_time": "3:58 PM EDT", "details_spans": "['drama|', '1 hr 45 mins|', '2003|']" }, { "date": "March 21", "name": "Fargo", "airing_time": "5:43 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 39 mins|', '1996|', 'HD|']" }, { "date": "March 21", "name": "Friday the 13th", "airing_time": "7:22 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']" }, { "date": "March 21", "name": "The Strangers: Prey at Night", "airing_time": "9:00 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 26 mins|', '2018|', 'HD|']" }, { "date": "March 21", "name": "Ghost", "airing_time": "10:26 PM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']" }, { "date": "March 21", "name": "Victor Frankenstein", "airing_time": "12:33 AM EDT", "details_spans": "['horror|', '1 hr 50 mins|', '2015|']" }, { "date": "March 21", "name": "I Am Wrath", "airing_time": "11:48 PM EDT", "details_spans": "['action|', '1 hr 31 mins|', '2016|']" }, { "date": "March 21", "name": "Rescue Dawn", "airing_time": "1:19 AM EDT", "details_spans": "['action|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 21", "name": "Mass Ave", "airing_time": "3:25 AM EDT", "details_spans": "['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']" }, { "date": "March 21", "name": "The Company You Keep", "airing_time": "3:46 AM EDT", "details_spans": "['suspense|', '2 hrs 2 mins|', '2013|']" }, { "date": "March 21", "name": "Another Country", "airing_time": "5:48 AM EDT", "details_spans": "['family relationships|', 'TV-MA|', '12 mins|', '2023|', 'HD|']" }, { "date": "March 21", "name": "Children of the Corn II: The Final Sacrifice", "airing_time": "6:00 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 34 mins|', '1993|', 'HD|']" }, { "date": "March 21", "name": "Predators", "airing_time": "7:34 AM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']" }, { "date": "March 21", "name": "Wes Craven Presents: They", "airing_time": "9:21 AM EDT", "details_spans": "['horror|', '1 hr 30 mins|', '2002|']" }, { "date": "March 21", "name": "Poltergeist II: The Other Side", "airing_time": "10:51 AM EDT", "details_spans": "['horror|', '1 hr 31 mins|', '1986|']" }, { "date": "March 21", "name": "Don't Let Go", "airing_time": "12:22 PM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 21", "name": "Barbershop 2: Back in Business", "airing_time": "2:06 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 46 mins|', '2004|', 'HD|']" }, { "date": "March 21", "name": "Line of Duty", "airing_time": "3:52 PM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2013|']" }, { "date": "March 21", "name": "Children of the Corn II: The Final Sacrifice", "airing_time": "5:33 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 34 mins|', '1993|', 'HD|']" }, { "date": "March 21", "name": "Office Space", "airing_time": "7:07 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 21", "name": "Children of the Corn 666: Isaac's Return", "airing_time": "8:37 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '1999|', 'HD|']" }, { "date": "March 21", "name": "House at the End of the Street", "airing_time": "10:00 PM EDT", "details_spans": "['horror|', '1 hr 41 mins|', '2012|']" }, { "date": "March 21", "name": "Let Me In", "airing_time": "11:41 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 21", "name": "The Pink Panther Strikes Again", "airing_time": "11:43 PM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 44 mins|', '1976|', 'HD|']" }, { "date": "March 21", "name": "Revenge of the Pink Panther", "airing_time": "1:27 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']" }, { "date": "March 21", "name": "Trail of the Pink Panther", "airing_time": "3:06 AM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 37 mins|', '1982|', 'HD|']" }, { "date": "March 21", "name": "Curse of the Pink Panther", "airing_time": "4:43 AM EDT", "details_spans": "['offbeat|', 'PG|', '1 hr 51 mins|', '1983|', 'HD|']" }, { "date": "March 21", "name": "The Rider", "airing_time": "6:34 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2018|']" }, { "date": "March 21", "name": "Chain Reaction", "airing_time": "8:17 AM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']" }, { "date": "March 21", "name": "Conviction", "airing_time": "10:04 AM EDT", "details_spans": "['drama|', '1 hr 47 mins|', '2010|']" }, { "date": "March 21", "name": "Mojave", "airing_time": "11:51 AM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2016|']" }, { "date": "March 21", "name": "The Show", "airing_time": "1:25 PM EDT", "details_spans": "['drama|', '1 hr 45 mins|', '2017|']" }, { "date": "March 21", "name": "The Bourne Ultimatum", "airing_time": "3:10 PM EDT", "details_spans": "['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "Friday the 13th Part V: A New Beginning", "airing_time": "5:06 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 21", "name": "Friday the 13th Part VI: Jason Lives", "airing_time": "6:39 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 28 mins|', '1986|', 'HD|']" }, { "date": "March 21", "name": "Freedomland", "airing_time": "8:07 PM EDT", "details_spans": "['big screen crime|', 'R|', '1 hr 53 mins|', '2006|', 'HD|']" }, { "date": "March 21", "name": "Whiteout", "airing_time": "10:00 PM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2009|']" }, { "date": "March 21", "name": "Altitude", "airing_time": "11:41 PM EDT", "details_spans": "['action|', '1 hr 28 mins|', '2017|']" }, { "date": "March 21", "name": "Enemy", "airing_time": "1:09 AM EDT", "details_spans": "['drama|', '1 hr 31 mins|', '2014|']" }, { "date": "March 21", "name": "Blue Valentine", "airing_time": "11:09 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 53 mins|', '2011|', 'HD|']" }, { "date": "March 21", "name": "Don't Let Go", "airing_time": "1:02 AM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 21", "name": "Posse: The Revenge of Jessie Lee", "airing_time": "2:46 AM EDT", "details_spans": "['action|', '1 hr 52 mins|', '1993|']" }, { "date": "March 21", "name": "Music Within", "airing_time": "4:38 AM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2007|']" }, { "date": "March 21", "name": "Teen Wolf", "airing_time": "6:12 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 21", "name": "Paranoia", "airing_time": "7:45 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']" }, { "date": "March 21", "name": "Hotel Artemis", "airing_time": "9:32 AM EDT", "details_spans": "['action|', '1 hr 34 mins|', '2018|']" }, { "date": "March 21", "name": "The Second Best Exotic Marigold Hotel", "airing_time": "11:06 AM EDT", "details_spans": "['comedy|', '2 hrs 3 mins|', '2015|']" }, { "date": "March 21", "name": "I Love You, Beth Cooper", "airing_time": "1:09 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 21", "name": "Missing in Action", "airing_time": "2:51 PM EDT", "details_spans": "['action|', '1 hr 42 mins|', '1984|']" }, { "date": "March 21", "name": "Woman Walks Ahead", "airing_time": "4:33 PM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2018|']" }, { "date": "March 21", "name": "Neh", "airing_time": "6:16 PM EDT", "details_spans": "['romantic dramas|', 'TV-14|', '16 mins|', '2021|', 'HD|']" }, { "date": "March 21", "name": "Unmothered", "airing_time": "6:32 PM EDT", "details_spans": "['dark comedy|', 'TV-PG|', '17 mins|', '2021|', 'HD|']" }, { "date": "March 21", "name": "Mud", "airing_time": "6:49 PM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 21", "name": "XXX", "airing_time": "9:00 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']" }, { "date": "March 21", "name": "Taxi", "airing_time": "11:05 PM EDT", "details_spans": "['action|', '1 hr 38 mins|', '2004|']" }, { "date": "March 21", "name": "Pet Sematary Two", "airing_time": "12:43 AM EDT", "details_spans": "['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']" }, { "date": "March 21", "name": "Hotel Mumbai", "airing_time": "11:58 PM EDT", "details_spans": "['action|', '2 hrs 4 mins|', '2019|']" }, { "date": "March 21", "name": "Trumbo", "airing_time": "2:02 AM EDT", "details_spans": "['biographical dramas|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Dark Places", "airing_time": "4:07 AM EDT", "details_spans": "['drama|', '1 hr 53 mins|', '2015|']" }, { "date": "March 21", "name": "Amazing Grace", "airing_time": "6:00 AM EDT", "details_spans": "['performances & concerts|', 'G|', '1 hr 29 mins|', '2018|', 'HD|']" }, { "date": "March 21", "name": "Taken 3", "airing_time": "7:29 AM EDT", "details_spans": "['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Charlie Wilson's War", "airing_time": "9:18 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 42 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "Across the Universe", "airing_time": "11:00 AM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 13 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "Into the Forest", "airing_time": "1:13 PM EDT", "details_spans": "['drama|', '1 hr 42 mins|', '2016|']" }, { "date": "March 21", "name": "Jennifer's Body", "airing_time": "2:55 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']" }, { "date": "March 21", "name": "The Last Movie Star", "airing_time": "4:38 PM EDT", "details_spans": "['drama|', '1 hr 44 mins|', '2018|']" }, { "date": "March 21", "name": "The Answer Man", "airing_time": "6:22 PM EDT", "details_spans": "['romantic comedy|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']" }, { "date": "March 21", "name": "The Last Word", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', '1 hr 48 mins|', '2017|']" }, { "date": "March 21", "name": "Into the Blue 2: The Reef", "airing_time": "9:48 PM EDT", "details_spans": "['suspense|', 'TV-MA|', '1 hr 31 mins|', '2009|']" }, { "date": "March 21", "name": "Tusk", "airing_time": "11:19 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2014|']" }, { "date": "March 21", "name": "The Last Movie Star", "airing_time": "1:01 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2018|']" }, { "date": "March 21", "name": "A Prayer Before Dawn", "airing_time": "12:00 AM EDT", "details_spans": "['action|', '1 hr 58 mins|', '2018|']" }, { "date": "March 21", "name": "Winter's Tale", "airing_time": "1:58 AM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 57 mins|', '2014|']" }, { "date": "March 21", "name": "When the Sun Sets", "airing_time": "3:55 AM EDT", "details_spans": "['family relationships|', 'TV-MA|', '15 mins|', '2022|', 'HD|']" }, { "date": "March 21", "name": "Extortion", "airing_time": "4:10 AM EDT", "details_spans": "['action|', '1 hr 50 mins|', '2017|']" }, { "date": "March 21", "name": "Nothing Like the Holidays", "airing_time": "6:00 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 21", "name": "Midnight Sun", "airing_time": "7:39 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '1 hr 32 mins|', '2018|', 'HD|']" }, { "date": "March 21", "name": "The Dead Don't Die", "airing_time": "9:11 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 21", "name": "Room", "airing_time": "10:56 AM EDT", "details_spans": "['family relationships|', 'R|', '1 hr 58 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Missing in Action 2 - The Beginning", "airing_time": "12:54 PM EDT", "details_spans": "['action|', '1 hr 36 mins|', '1985|']" }, { "date": "March 21", "name": "I Am Wrath", "airing_time": "2:30 PM EDT", "details_spans": "['action|', '1 hr 31 mins|', '2016|']" }, { "date": "March 21", "name": "The Rage: Carrie 2", "airing_time": "4:01 PM EDT", "details_spans": "['occult|', 'R|', '1 hr 46 mins|', '1999|', 'HD|']" }, { "date": "March 21", "name": "Baby Mama", "airing_time": "5:47 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 40 mins|', '2008|', 'HD|']" }, { "date": "March 21", "name": "Straight Outta Compton", "airing_time": "7:27 PM EDT", "details_spans": "['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Fifty Shades of Grey", "airing_time": "9:54 PM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Attack the Block", "airing_time": "12:00 AM EDT", "details_spans": "['action|', '1 hr 29 mins|', '2011|']" }, { "date": "March 21", "name": "Cyrus", "airing_time": "1:29 AM EDT", "details_spans": "['romance|', '1 hr 31 mins|', '2010|']" }, { "date": "March 21", "name": "Carrie", "airing_time": "11:14 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 21", "name": "I Love You, Beth Cooper", "airing_time": "12:54 AM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 21", "name": "Amy", "airing_time": "2:36 AM EDT", "details_spans": "['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']" }, { "date": "March 21", "name": "Dim Sum Funeral", "airing_time": "4:44 AM EDT", "details_spans": "['drama|', '1 hr 37 mins|', '2009|']" }, { "date": "March 21", "name": "Pieces of April", "airing_time": "6:21 AM EDT", "details_spans": "['comedy|', '1 hr 21 mins|', '2003|']" }, { "date": "March 21", "name": "Paparazzi", "airing_time": "7:42 AM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2004|']" }, { "date": "March 21", "name": "Buffy the Vampire Slayer", "airing_time": "9:07 AM EDT", "details_spans": "['teen comedy|', 'PG-13|', '1 hr 26 mins|', '1992|', 'HD|']" }, { "date": "March 21", "name": "Barbershop", "airing_time": "10:33 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 21", "name": "XXX: State of the Union", "airing_time": "12:16 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 21", "name": "Kingsman: The Secret Service", "airing_time": "1:57 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 21", "name": "Fifty Shades Darker", "airing_time": "4:06 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 21", "name": "I Now Pronounce You Chuck & Larry", "airing_time": "6:04 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 21", "name": "Election", "airing_time": "8:00 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 43 mins|', '1999|', 'HD|']" }, { "date": "March 21", "name": "Shiva Baby", "airing_time": "9:43 PM EDT", "details_spans": "['drama|', 'TV-MA|', '1 hr 18 mins|', '2021|', 'HD|']" }, { "date": "March 21", "name": "Unbreakable", "airing_time": "11:01 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 21", "name": "Fast Color", "airing_time": "12:48 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2019|']" }, { "date": "March 22", "name": "Unbreakable", "airing_time": "11:01 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 22", "name": "Fast Color", "airing_time": "12:48 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2019|']" }, { "date": "March 22", "name": "The Field Guide to Evil", "airing_time": "2:31 AM EDT", "details_spans": "['horror|', '1 hr 58 mins|', '2019|']" }, { "date": "March 22", "name": "Ginger & Rosa", "airing_time": "4:29 AM EDT", "details_spans": "['drama|', '1 hr 31 mins|', '2013|']" }, { "date": "March 22", "name": "Line of Duty", "airing_time": "6:00 AM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2013|']" }, { "date": "March 22", "name": "Philomena", "airing_time": "7:41 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']" }, { "date": "March 22", "name": "Teen Wolf", "airing_time": "9:19 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 22", "name": "Beauty Shop", "airing_time": "10:52 AM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']" }, { "date": "March 22", "name": "Slums of Beverly Hills", "airing_time": "12:38 PM EDT", "details_spans": "['comedy|', '1 hr 32 mins|', '1998|']" }, { "date": "March 22", "name": "Fifty Shades Freed", "airing_time": "2:10 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']" }, { "date": "March 22", "name": "XXX", "airing_time": "3:56 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']" }, { "date": "March 22", "name": "Paycheck", "airing_time": "6:01 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']" }, { "date": "March 22", "name": "Vacation", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 40 mins|', '2015|', 'UHD|']" }, { "date": "March 22", "name": "Observe and Report", "airing_time": "9:40 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']" }, { "date": "March 22", "name": "Yes Man", "airing_time": "11:07 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 22", "name": "Body of Lies", "airing_time": "12:52 AM EDT", "details_spans": "['action|', '2 hrs 9 mins|', '2008|']" }, { "date": "March 22", "name": "Ghost", "airing_time": "10:26 PM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']" }, { "date": "March 22", "name": "Victor Frankenstein", "airing_time": "12:33 AM EDT", "details_spans": "['horror|', '1 hr 50 mins|', '2015|']" }, { "date": "March 22", "name": "The Witch", "airing_time": "2:23 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 33 mins|', '2016|', 'HD|']" }, { "date": "March 22", "name": "A Most Violent Year", "airing_time": "3:56 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 6 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "The Gift", "airing_time": "6:02 AM EDT", "details_spans": "['drama|', 'TV-14|', '15 mins|', '2022|', 'HD|']" }, { "date": "March 22", "name": "Walk the Line", "airing_time": "6:17 AM EDT", "details_spans": "['romantic dramas|', 'PG-13|', '2 hrs 17 mins|', '2005|', 'HD|']" }, { "date": "March 22", "name": "The Captive", "airing_time": "8:34 AM EDT", "details_spans": "['drama|', '16+|', '1 hr 53 mins|', '2014|', 'HD|']" }, { "date": "March 22", "name": "Gemini", "airing_time": "10:27 AM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2018|']" }, { "date": "March 22", "name": "Mississippi Grind", "airing_time": "12:01 PM EDT", "details_spans": "['comedy|', '1 hr 50 mins|', '2015|']" }, { "date": "March 22", "name": "The Forbidden Kingdom", "airing_time": "1:51 PM EDT", "details_spans": "['action|', '1 hr 45 mins|', '2008|']" }, { "date": "March 22", "name": "The Visit", "airing_time": "3:36 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 34 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "Appaloosa", "airing_time": "5:10 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 56 mins|', '2008|', 'HD|']" }, { "date": "March 22", "name": "Dark Places", "airing_time": "7:06 PM EDT", "details_spans": "['drama|', '1 hr 54 mins|', '2015|']" }, { "date": "March 22", "name": "Taken 3", "airing_time": "9:00 PM EDT", "details_spans": "['crime|', 'PG-13|', '1 hr 49 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "Mojave", "airing_time": "10:49 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2016|']" }, { "date": "March 22", "name": "Green Room", "airing_time": "12:23 AM EDT", "details_spans": "['horror|', '1 hr 36 mins|', '2016|']" }, { "date": "March 22", "name": "Let Me In", "airing_time": "11:41 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 22", "name": "My Bodyguard", "airing_time": "1:37 AM EDT", "details_spans": "['comedy|', '1 hr 37 mins|', '1980|']" }, { "date": "March 22", "name": "Slice", "airing_time": "3:14 AM EDT", "details_spans": "['comedy|', '1 hr 23 mins|', '2018|']" }, { "date": "March 22", "name": "Children of the Corn 666: Isaac's Return", "airing_time": "4:37 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '1999|', 'HD|']" }, { "date": "March 22", "name": "Carrie", "airing_time": "6:00 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 39 mins|', '1976|', 'HD|']" }, { "date": "March 22", "name": "Poltergeist (2015)", "airing_time": "7:39 AM EDT", "details_spans": "['fantasy-sci-fi|', '1 hr 34 mins|', '2015|']" }, { "date": "March 22", "name": "Unbreakable", "airing_time": "9:13 AM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 22", "name": "The Edge", "airing_time": "11:00 AM EDT", "details_spans": "['suspense|', '1 hr 58 mins|', '1997|']" }, { "date": "March 22", "name": "Leatherheads", "airing_time": "12:58 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 22", "name": "John Dies at the End", "airing_time": "2:52 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 22", "name": "Talk Black", "airing_time": "4:32 PM EDT", "details_spans": "['comedy|', 'TV-14|', '14 mins|', '2023|', 'HD|']" }, { "date": "March 22", "name": "Rescue Dawn", "airing_time": "4:46 PM EDT", "details_spans": "['action|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 22", "name": "Taxi", "airing_time": "6:52 PM EDT", "details_spans": "['action|', '1 hr 38 mins|', '2004|']" }, { "date": "March 22", "name": "Office Space", "airing_time": "8:30 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 22", "name": "The Rover", "airing_time": "10:00 PM EDT", "details_spans": "['action|', 'R|', '1 hr 43 mins|', '2014|', 'HD|']" }, { "date": "March 22", "name": "John Dies at the End", "airing_time": "11:43 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 22", "name": "Strange Days", "airing_time": "1:23 AM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 22", "name": "Altitude", "airing_time": "11:41 PM EDT", "details_spans": "['action|', '1 hr 28 mins|', '2017|']" }, { "date": "March 22", "name": "Enemy", "airing_time": "1:09 AM EDT", "details_spans": "['drama|', '1 hr 31 mins|', '2014|']" }, { "date": "March 22", "name": "Ronin", "airing_time": "2:40 AM EDT", "details_spans": "['action|', '2 hrs 2 mins|', '1998|']" }, { "date": "March 22", "name": "A View to a Kill", "airing_time": "4:42 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']" }, { "date": "March 22", "name": "Source Code", "airing_time": "6:54 AM EDT", "details_spans": "['suspense|', '1 hr 33 mins|', '2011|']" }, { "date": "March 22", "name": "The Pink Panther", "airing_time": "8:27 AM EDT", "details_spans": "['offbeat|', 'TV-PG|', '1 hr 56 mins|', '1964|', 'HD|']" }, { "date": "March 22", "name": "A Shot in the Dark", "airing_time": "10:23 AM EDT", "details_spans": "['big screen crime|', 'PG|', '1 hr 43 mins|', '1964|', 'HD|']" }, { "date": "March 22", "name": "Locke", "airing_time": "12:06 PM EDT", "details_spans": "['drama|', '1 hr 25 mins|', '2014|']" }, { "date": "March 22", "name": "Pride and Glory", "airing_time": "1:31 PM EDT", "details_spans": "['drama|', '2 hrs 11 mins|', '2008|']" }, { "date": "March 22", "name": "Revenge of the Pink Panther", "airing_time": "3:42 PM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 39 mins|', '1978|', 'HD|']" }, { "date": "March 22", "name": "Friday the 13th Part VII: The New Blood", "airing_time": "5:21 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 29 mins|', '1988|', 'HD|']" }, { "date": "March 22", "name": "Friday the 13th Part VIII: Jason Takes Manhattan", "airing_time": "6:50 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 41 mins|', '1989|', 'HD|']" }, { "date": "March 22", "name": "Scary Movie", "airing_time": "8:31 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 29 mins|', '2000|', 'HD|']" }, { "date": "March 22", "name": "Scary Movie 2", "airing_time": "10:00 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 23 mins|', '2001|', 'HD|']" }, { "date": "March 22", "name": "Scary Movie 3", "airing_time": "11:23 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']" }, { "date": "March 22", "name": "Ex Machina", "airing_time": "12:48 AM EDT", "details_spans": "['artificial intelligence & robots|', 'R|', '1 hr 49 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "Taxi", "airing_time": "11:05 PM EDT", "details_spans": "['action|', '1 hr 38 mins|', '2004|']" }, { "date": "March 22", "name": "Pet Sematary Two", "airing_time": "12:43 AM EDT", "details_spans": "['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']" }, { "date": "March 22", "name": "Slums of Beverly Hills", "airing_time": "2:24 AM EDT", "details_spans": "['comedy|', '1 hr 32 mins|', '1998|']" }, { "date": "March 22", "name": "The Verdict", "airing_time": "3:56 AM EDT", "details_spans": "['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']" }, { "date": "March 22", "name": "The Sentinel", "airing_time": "6:05 AM EDT", "details_spans": "['action|', '1 hr 48 mins|', '2006|']" }, { "date": "March 22", "name": "Elizabethtown", "airing_time": "7:53 AM EDT", "details_spans": "['comedy|', '2 hrs 4 mins|', '2005|']" }, { "date": "March 22", "name": "Leatherheads", "airing_time": "9:57 AM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 22", "name": "Predators", "airing_time": "11:51 AM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 48 mins|', '2010|', 'HD|']" }, { "date": "March 22", "name": "Rescue Dawn", "airing_time": "1:39 PM EDT", "details_spans": "['action|', '2 hrs 6 mins|', '2007|']" }, { "date": "March 22", "name": "The Family Meeting", "airing_time": "3:45 PM EDT", "details_spans": "['family relationships|', 'TV-14|', '16 mins|', '2023|', 'HD|']" }, { "date": "March 22", "name": "Pet Sematary", "airing_time": "4:01 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 43 mins|', '1989|', 'HD|']" }, { "date": "March 22", "name": "The Pick-Up Artist", "airing_time": "5:44 PM EDT", "details_spans": "['comedy|', '1 hr 22 mins|', '1987|']" }, { "date": "March 22", "name": "Welcome Home Roscoe Jenkins", "airing_time": "7:06 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2008|', 'HD|']" }, { "date": "March 22", "name": "Barbershop", "airing_time": "9:00 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 43 mins|', '2002|', 'HD|']" }, { "date": "March 22", "name": "Life After Beth", "airing_time": "10:43 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']" }, { "date": "March 22", "name": "The Dead Don't Die", "airing_time": "12:12 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 22", "name": "Tusk", "airing_time": "11:19 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2014|']" }, { "date": "March 22", "name": "The Last Movie Star", "airing_time": "1:01 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2018|']" }, { "date": "March 22", "name": "Death Wish 4: The Crackdown", "airing_time": "2:44 AM EDT", "details_spans": "['action|', 'R|', '1 hr 40 mins|', '1987|', 'HD|']" }, { "date": "March 22", "name": "Evan Almighty", "airing_time": "4:24 AM EDT", "details_spans": "['kids & family|', 'PG|', '1 hr 36 mins|', '2007|', 'HD|']" }, { "date": "March 22", "name": "For Your Eyes Only", "airing_time": "6:00 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']" }, { "date": "March 22", "name": "A Most Violent Year", "airing_time": "8:08 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "The Return of the Pink Panther", "airing_time": "10:13 AM EDT", "details_spans": "['big screen crime|', 'G|', '1 hr 52 mins|', '1975|', 'HD|']" }, { "date": "March 22", "name": "Curse of the Pink Panther", "airing_time": "12:05 PM EDT", "details_spans": "['offbeat|', 'PG|', '1 hr 51 mins|', '1983|', 'HD|']" }, { "date": "March 22", "name": "Goldfinger", "airing_time": "1:56 PM EDT", "details_spans": "['action|', 'PG|', '1 hr 51 mins|', '1964|', 'HD|']" }, { "date": "March 22", "name": "On Her Majesty's Secret Service", "airing_time": "3:47 PM EDT", "details_spans": "['adventure|', 'PG|', '2 hrs 23 mins|', '1969|', 'HD|']" }, { "date": "March 22", "name": "Dumb and Dumber To", "airing_time": "6:10 PM EDT", "details_spans": "['dark comedy|', 'PG-13|', '1 hr 50 mins|', '2014|', 'HD|']" }, { "date": "March 22", "name": "Hot Summer Nights", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', '1 hr 48 mins|', '2018|']" }, { "date": "March 22", "name": "For Your Eyes Only", "airing_time": "9:48 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 8 mins|', '1981|', 'HD|']" }, { "date": "March 22", "name": "The Bourne Ultimatum", "airing_time": "11:56 PM EDT", "details_spans": "['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 22", "name": "Attack the Block", "airing_time": "12:00 AM EDT", "details_spans": "['action|', '1 hr 29 mins|', '2011|']" }, { "date": "March 22", "name": "Cyrus", "airing_time": "1:29 AM EDT", "details_spans": "['romance|', '1 hr 31 mins|', '2010|']" }, { "date": "March 22", "name": "Observe and Report", "airing_time": "3:00 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']" }, { "date": "March 22", "name": "Poltergeist III", "airing_time": "4:27 AM EDT", "details_spans": "['horror|', '1 hr 38 mins|', '1988|']" }, { "date": "March 22", "name": "Johnny Dangerously", "airing_time": "6:05 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 31 mins|', '1984|', 'HD|']" }, { "date": "March 22", "name": "Mud", "airing_time": "7:36 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 11 mins|', '2013|', 'HD|']" }, { "date": "March 22", "name": "House at the End of the Street", "airing_time": "9:47 AM EDT", "details_spans": "['horror|', '1 hr 42 mins|', '2012|']" }, { "date": "March 22", "name": "How to Talk to Girls at Parties", "airing_time": "11:29 AM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']" }, { "date": "March 22", "name": "McQueen", "airing_time": "1:12 PM EDT", "details_spans": "['documentary|', '1 hr 52 mins|', '2018|']" }, { "date": "March 22", "name": "Internal Affairs", "airing_time": "3:04 PM EDT", "details_spans": "['drama|', '1 hr 55 mins|', '1990|']" }, { "date": "March 22", "name": "Paparazzi", "airing_time": "4:59 PM EDT", "details_spans": "['drama|', '1 hr 26 mins|', '2004|']" }, { "date": "March 22", "name": "Pet Sematary Two", "airing_time": "6:25 PM EDT", "details_spans": "['creatures|', 'R|', '1 hr 41 mins|', '1992|', 'HD|']" }, { "date": "March 22", "name": "Kingsman: The Secret Service", "airing_time": "8:06 PM EDT", "details_spans": "['high octane thrillers|', 'R|', '2 hrs 9 mins|', '2015|', 'HD|']" }, { "date": "March 22", "name": "She's Out of My League", "airing_time": "10:15 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']" }, { "date": "March 22", "name": "The Adderall Diaries", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 27 mins|', '2016|', 'HD|']" }, { "date": "March 22", "name": "Children of the Corn V: Fields of Terror", "airing_time": "1:27 AM EDT", "details_spans": "['horror|', 'TV-MA|', '1 hr 24 mins|', '1998|', 'HD|']" }, { "date": "March 22", "name": "Unbreakable", "airing_time": "11:01 PM EDT", "details_spans": "['thrillers|', 'PG-13|', '1 hr 47 mins|', '2000|', 'HD|']" }, { "date": "March 22", "name": "Fast Color", "airing_time": "12:48 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2019|']" }, { "date": "March 22", "name": "A Prayer Before Dawn", "airing_time": "2:31 AM EDT", "details_spans": "['action|', '1 hr 58 mins|', '2018|']" }, { "date": "March 22", "name": "Ginger & Rosa", "airing_time": "4:29 AM EDT", "details_spans": "['drama|', '1 hr 31 mins|', '2013|']" }, { "date": "March 22", "name": "Line of Duty", "airing_time": "6:00 AM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2013|']" }, { "date": "March 22", "name": "Philomena", "airing_time": "7:41 AM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 38 mins|', '2013|', 'HD|']" }, { "date": "March 22", "name": "Teen Wolf", "airing_time": "9:19 AM EDT", "details_spans": "['comedy|', 'PG|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 22", "name": "Beauty Shop", "airing_time": "10:52 AM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 46 mins|', '2005|', 'HD|']" }, { "date": "March 22", "name": "Slums of Beverly Hills", "airing_time": "12:38 PM EDT", "details_spans": "['comedy|', '1 hr 32 mins|', '1998|']" }, { "date": "March 22", "name": "Fifty Shades Freed", "airing_time": "2:10 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 46 mins|', '2018|', 'HD|']" }, { "date": "March 22", "name": "XXX", "airing_time": "3:56 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '2 hrs 5 mins|', '2002|', 'HD|']" }, { "date": "March 22", "name": "Paycheck", "airing_time": "6:01 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']" }, { "date": "March 22", "name": "Vacation", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 40 mins|', '2015|', 'UHD|']" }, { "date": "March 22", "name": "Observe and Report", "airing_time": "9:40 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 27 mins|', '2009|', 'HD|']" }, { "date": "March 22", "name": "Yes Man", "airing_time": "11:07 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 22", "name": "The Verdict", "airing_time": "12:52 AM EDT", "details_spans": "['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']" }, { "date": "March 23", "name": "Yes Man", "airing_time": "11:07 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 23", "name": "Body of Lies", "airing_time": "12:52 AM EDT", "details_spans": "['action|', '2 hrs 9 mins|', '2008|']" }, { "date": "March 23", "name": "The Vanishing of Sidney Hall", "airing_time": "3:01 AM EDT", "details_spans": "['drama|', '2 hrs 1 min|', '2018|']" }, { "date": "March 23", "name": "Killing Me Softly", "airing_time": "5:02 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 41 mins|', '2003|', 'HD|']" }, { "date": "March 23", "name": "The Company You Keep", "airing_time": "6:43 AM EDT", "details_spans": "['suspense|', '2 hrs 2 mins|', '2013|']" }, { "date": "March 23", "name": "Don't Let Go", "airing_time": "8:45 AM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 23", "name": "The Dead Don't Die", "airing_time": "10:29 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 23", "name": "Strange Days", "airing_time": "12:14 PM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 23", "name": "She's Out of My League", "airing_time": "2:40 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']" }, { "date": "March 23", "name": "Let Me In", "airing_time": "4:25 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 23", "name": "Baby Mama", "airing_time": "6:21 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 23", "name": "Office Space", "airing_time": "8:00 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 23", "name": "How to Talk to Girls at Parties", "airing_time": "9:30 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2017|', 'HD|']" }, { "date": "March 23", "name": "I Love You, Beth Cooper", "airing_time": "11:13 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 23", "name": "Elizabethtown", "airing_time": "12:55 AM EDT", "details_spans": "['comedy|', '2 hrs 4 mins|', '2005|']" }, { "date": "March 23", "name": "Mojave", "airing_time": "10:49 PM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2016|']" }, { "date": "March 23", "name": "Green Room", "airing_time": "12:23 AM EDT", "details_spans": "['horror|', '1 hr 36 mins|', '2016|']" }, { "date": "March 23", "name": "Friday the 13th Part V: A New Beginning", "airing_time": "1:59 AM EDT", "details_spans": "['slashers|', 'R|', '1 hr 33 mins|', '1985|', 'HD|']" }, { "date": "March 23", "name": "Angel of Mine", "airing_time": "3:32 AM EDT", "details_spans": "['drama|', '1 hr 39 mins|', '2019|']" }, { "date": "March 23", "name": "Across the Universe", "airing_time": "5:11 AM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 14 mins|', '2007|', 'HD|']" }, { "date": "March 23", "name": "The New World", "airing_time": "7:25 AM EDT", "details_spans": "['drama|', '2 hrs 16 mins|', '2005|']" }, { "date": "March 23", "name": "F/x", "airing_time": "9:41 AM EDT", "details_spans": "['action|', 'R|', '1 hr 49 mins|', '1986|', 'HD|']" }, { "date": "March 23", "name": "A View to a Kill", "airing_time": "11:30 AM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 12 mins|', '1985|', 'HD|']" }, { "date": "March 23", "name": "The Frozen Ground", "airing_time": "1:42 PM EDT", "details_spans": "['drama|', '1 hr 46 mins|', '2013|']" }, { "date": "March 23", "name": "The Bourne Ultimatum", "airing_time": "3:28 PM EDT", "details_spans": "['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 23", "name": "Antitrust", "airing_time": "5:24 PM EDT", "details_spans": "['adventure|', 'PG-13|', '1 hr 49 mins|', '2001|', 'HD|']" }, { "date": "March 23", "name": "Chain Reaction", "airing_time": "7:13 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 47 mins|', '1996|', 'HD|']" }, { "date": "March 23", "name": "Source Code", "airing_time": "9:00 PM EDT", "details_spans": "['suspense|', '1 hr 33 mins|', '2011|']" }, { "date": "March 23", "name": "Whiteout", "airing_time": "10:33 PM EDT", "details_spans": "['action|', '1 hr 41 mins|', '2009|']" }, { "date": "March 23", "name": "Tusk", "airing_time": "12:14 AM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2014|']" }, { "date": "March 23", "name": "John Dies at the End", "airing_time": "11:43 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 40 mins|', '2013|', 'HD|']" }, { "date": "March 23", "name": "Strange Days", "airing_time": "1:23 AM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 23", "name": "Extortion", "airing_time": "3:49 AM EDT", "details_spans": "['action|', '1 hr 50 mins|', '2017|']" }, { "date": "March 23", "name": "Mass Ave", "airing_time": "5:39 AM EDT", "details_spans": "['drama|', 'TV-14|', '21 mins|', '2022|', 'HD|']" }, { "date": "March 23", "name": "The Rage: Carrie 2", "airing_time": "6:00 AM EDT", "details_spans": "['occult|', 'R|', '1 hr 45 mins|', '1999|', 'HD|']" }, { "date": "March 23", "name": "Poltergeist III", "airing_time": "7:45 AM EDT", "details_spans": "['horror|', '1 hr 38 mins|', '1988|']" }, { "date": "March 23", "name": "Missing in Action", "airing_time": "9:23 AM EDT", "details_spans": "['action|', '1 hr 42 mins|', '1984|']" }, { "date": "March 23", "name": "Braddock: Missing in Action III", "airing_time": "11:05 AM EDT", "details_spans": "['action|', '1 hr 44 mins|', '1988|']" }, { "date": "March 23", "name": "Leatherheads", "airing_time": "12:49 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 54 mins|', '2016|', 'HD|']" }, { "date": "March 23", "name": "Major League", "airing_time": "2:43 PM EDT", "details_spans": "['classic comedy|', 'R|', '1 hr 47 mins|', '1989|', 'HD|']" }, { "date": "March 23", "name": "Brokedown Palace", "airing_time": "4:30 PM EDT", "details_spans": "['drama|', '1 hr 41 mins|', '1999|']" }, { "date": "March 23", "name": "Extortion", "airing_time": "6:11 PM EDT", "details_spans": "['action|', '1 hr 50 mins|', '2017|']" }, { "date": "March 23", "name": "Paycheck", "airing_time": "8:01 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 59 mins|', '2003|', 'HD|']" }, { "date": "March 23", "name": "Paranoia", "airing_time": "10:00 PM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 47 mins|', '2013|', 'HD|']" }, { "date": "March 23", "name": "American Heist", "airing_time": "11:47 PM EDT", "details_spans": "['action|', '1 hr 36 mins|', '2015|']" }, { "date": "March 23", "name": "Missing in Action", "airing_time": "1:23 AM EDT", "details_spans": "['action|', '1 hr 42 mins|', '1984|']" }, { "date": "March 23", "name": "Scary Movie 3", "airing_time": "11:23 PM EDT", "details_spans": "['slashers|', 'PG-13|', '1 hr 25 mins|', '2003|', 'HD|']" }, { "date": "March 23", "name": "Ex Machina", "airing_time": "12:48 AM EDT", "details_spans": "['artificial intelligence & robots|', 'R|', '1 hr 49 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Trumbo", "airing_time": "2:37 AM EDT", "details_spans": "['biographical dramas|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Into the Forest", "airing_time": "4:42 AM EDT", "details_spans": "['drama|', '1 hr 42 mins|', '2016|']" }, { "date": "March 23", "name": "Little Woods", "airing_time": "6:24 AM EDT", "details_spans": "['suspense|', '1 hr 44 mins|', '2019|']" }, { "date": "March 23", "name": "Leap of Faith", "airing_time": "8:08 AM EDT", "details_spans": "['comedy|', '1 hr 48 mins|', '1992|']" }, { "date": "March 23", "name": "Ghost", "airing_time": "9:56 AM EDT", "details_spans": "['drama|', 'PG-13|', '2 hrs 7 mins|', '1990|', 'HD|']" }, { "date": "March 23", "name": "The Strangers: Prey at Night", "airing_time": "12:03 PM EDT", "details_spans": "['horror|', 'R|', '1 hr 25 mins|', '2018|', 'HD|']" }, { "date": "March 23", "name": "Altitude", "airing_time": "1:28 PM EDT", "details_spans": "['action|', '1 hr 28 mins|', '2017|']" }, { "date": "March 23", "name": "Conviction", "airing_time": "2:56 PM EDT", "details_spans": "['drama|', '1 hr 48 mins|', '2010|']" }, { "date": "March 23", "name": "Blown Away", "airing_time": "4:44 PM EDT", "details_spans": "['drama|', 'R|', '2 hrs 1 min|', '1994|', 'HD|']" }, { "date": "March 23", "name": "Friday the 13th", "airing_time": "6:45 PM EDT", "details_spans": "['slashers|', 'R|', '1 hr 38 mins|', '2009|', 'HD|']" }, { "date": "March 23", "name": "Climax", "airing_time": "8:23 PM EDT", "details_spans": "['horror|', '1 hr 37 mins|', '2019|']" }, { "date": "March 23", "name": "Jennifer's Body", "airing_time": "10:00 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 43 mins|', '2009|', 'HD|']" }, { "date": "March 23", "name": "Internal Affairs", "airing_time": "11:43 PM EDT", "details_spans": "['drama|', '1 hr 55 mins|', '1990|']" }, { "date": "March 23", "name": "Life After Beth", "airing_time": "10:43 PM EDT", "details_spans": "['comedy|', 'R|', '1 hr 29 mins|', '2014|', 'HD|']" }, { "date": "March 23", "name": "The Dead Don't Die", "airing_time": "12:12 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 23", "name": "Missing in Action 2 - The Beginning", "airing_time": "1:57 AM EDT", "details_spans": "['action|', '1 hr 36 mins|', '1985|']" }, { "date": "March 23", "name": "Braddock: Missing in Action III", "airing_time": "3:33 AM EDT", "details_spans": "['action|', '1 hr 44 mins|', '1988|']" }, { "date": "March 23", "name": "Laggies", "airing_time": "5:17 AM EDT", "details_spans": "['comedy|', '1 hr 40 mins|', '2014|']" }, { "date": "March 23", "name": "Dim Sum Funeral", "airing_time": "6:57 AM EDT", "details_spans": "['drama|', '1 hr 37 mins|', '2009|']" }, { "date": "March 23", "name": "Equals", "airing_time": "8:34 AM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2016|']" }, { "date": "March 23", "name": "I Am Wrath", "airing_time": "10:17 AM EDT", "details_spans": "['action|', '1 hr 31 mins|', '2016|']" }, { "date": "March 23", "name": "Barbershop 2: Back in Business", "airing_time": "11:48 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 46 mins|', '2004|', 'HD|']" }, { "date": "March 23", "name": "Room", "airing_time": "1:34 PM EDT", "details_spans": "['family relationships|', 'R|', '1 hr 58 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Luce", "airing_time": "3:32 PM EDT", "details_spans": "['drama|', '1 hr 50 mins|', '2019|']" }, { "date": "March 23", "name": "Pure", "airing_time": "5:22 PM EDT", "details_spans": "['romantic dramas|', 'TV-14|', '13 mins|', '2022|', 'HD|']" }, { "date": "March 23", "name": "Down to Earth", "airing_time": "5:35 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 28 mins|', '2001|', 'HD|']" }, { "date": "March 23", "name": "Hustle & Flow", "airing_time": "7:03 PM EDT", "details_spans": "['crime|', 'R|', '1 hr 57 mins|', '2005|', 'HD|']" }, { "date": "March 23", "name": "Straight Outta Compton", "airing_time": "9:00 PM EDT", "details_spans": "['biographical dramas|', 'R|', '2 hrs 27 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Poltergeist II: The Other Side", "airing_time": "11:27 PM EDT", "details_spans": "['horror|', '1 hr 31 mins|', '1986|']" }, { "date": "March 23", "name": "Beauty Shop", "airing_time": "12:58 AM EDT", "details_spans": "['family relationships|', 'PG-13|', '1 hr 47 mins|', '2005|', 'HD|']" }, { "date": "March 23", "name": "The Bourne Ultimatum", "airing_time": "11:56 PM EDT", "details_spans": "['action|', 'PG-13|', '1 hr 56 mins|', '2007|', 'HD|']" }, { "date": "March 23", "name": "Bandits", "airing_time": "1:52 AM EDT", "details_spans": "['crime|', '2 hrs 3 mins|', '2001|']" }, { "date": "March 23", "name": "A Most Violent Year", "airing_time": "3:55 AM EDT", "details_spans": "['drama|', 'R|', '2 hrs 5 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Mermaids", "airing_time": "6:00 AM EDT", "details_spans": "['drama|', 'PG-13|', '1 hr 50 mins|', '1990|', 'HD|']" }, { "date": "March 23", "name": "Friday the 13th", "airing_time": "7:50 AM EDT", "details_spans": "['horror|', 'R|', '1 hr 36 mins|', '1980|', 'HD|']" }, { "date": "March 23", "name": "Friday the 13th-The Final Chapter", "airing_time": "9:26 AM EDT", "details_spans": "['slashers|', 'R|', '1 hr 31 mins|', '1984|', 'HD|']" }, { "date": "March 23", "name": "A Night at the Roxbury", "airing_time": "10:57 AM EDT", "details_spans": "['offbeat|', 'PG-13|', '1 hr 22 mins|', '1998|', 'HD|']" }, { "date": "March 23", "name": "The Spy Who Loved Me", "airing_time": "12:19 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 6 mins|', '1977|', 'HD|']" }, { "date": "March 23", "name": "Never Say Never Again", "airing_time": "2:25 PM EDT", "details_spans": "['high octane thrillers|', 'PG|', '2 hrs 14 mins|', '1983|', 'HD|']" }, { "date": "March 23", "name": "Into the Blue", "airing_time": "4:39 PM EDT", "details_spans": "['action|', '1 hr 51 mins|', '2005|']" }, { "date": "March 23", "name": "The Goods: Live Hard, Sell Hard", "airing_time": "6:30 PM EDT", "details_spans": "['comedy|', '1 hr 30 mins|', '2009|']" }, { "date": "March 23", "name": "Paper Towns", "airing_time": "8:00 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 50 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Lean on Pete", "airing_time": "9:50 PM EDT", "details_spans": "['drama|', '12|', '2 hrs 2 mins|', '2018|', 'HD|']" }, { "date": "March 23", "name": "De-Lovely", "airing_time": "11:52 PM EDT", "details_spans": "['musicals|', 'PG-13|', '2 hrs 6 mins|', '2004|', 'HD|']" }, { "date": "March 23", "name": "The Adderall Diaries", "airing_time": "12:00 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 27 mins|', '2016|', 'HD|']" }, { "date": "March 23", "name": "Children of the Corn V: Fields of Terror", "airing_time": "1:27 AM EDT", "details_spans": "['horror|', 'TV-MA|', '1 hr 24 mins|', '1998|', 'HD|']" }, { "date": "March 23", "name": "Obvious Child", "airing_time": "2:51 AM EDT", "details_spans": "['comedy|', '1 hr 25 mins|', '2014|']" }, { "date": "March 23", "name": "Cracked", "airing_time": "4:16 AM EDT", "details_spans": "['romantic dramas|', 'TV-MA|', '16 mins|', '2022|', 'HD|']" }, { "date": "March 23", "name": "Music Within", "airing_time": "4:32 AM EDT", "details_spans": "['drama|', '1 hr 34 mins|', '2007|']" }, { "date": "March 23", "name": "Master of the Drunken Fist: Beggar So", "airing_time": "6:06 AM EDT", "details_spans": "['action|', 'TV-14|', '1 hr 38 mins|', '2017|']" }, { "date": "March 23", "name": "The War of the Roses", "airing_time": "7:44 AM EDT", "details_spans": "['comedy|', '1 hr 57 mins|', '1989|']" }, { "date": "March 23", "name": "The Promise", "airing_time": "9:41 AM EDT", "details_spans": "['action|', 'PG-13|', '2 hrs 13 mins|', '2017|', 'HD|']" }, { "date": "March 23", "name": "Wes Craven Presents: They", "airing_time": "11:54 AM EDT", "details_spans": "['horror|', '1 hr 29 mins|', '2002|']" }, { "date": "March 23", "name": "Predators", "airing_time": "1:23 PM EDT", "details_spans": "['space & beyond|', 'R|', '1 hr 47 mins|', '2010|', 'HD|']" }, { "date": "March 23", "name": "XXX: State of the Union", "airing_time": "3:10 PM EDT", "details_spans": "['high octane thrillers|', 'PG-13|', '1 hr 41 mins|', '2005|', 'HD|']" }, { "date": "March 23", "name": "Fifty Shades Darker", "airing_time": "4:51 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 58 mins|', '2017|', 'HD|']" }, { "date": "March 23", "name": "Fifty Shades Freed", "airing_time": "6:49 PM EDT", "details_spans": "['drama|', 'R|', '1 hr 45 mins|', '2018|', 'HD|']" }, { "date": "March 23", "name": "Slice", "airing_time": "8:34 PM EDT", "details_spans": "['comedy|', '1 hr 23 mins|', '2018|']" }, { "date": "March 23", "name": "Black Mass", "airing_time": "9:57 PM EDT", "details_spans": "['organized crime|', 'R|', '2 hrs 3 mins|', '2015|', 'HD|']" }, { "date": "March 23", "name": "Amy", "airing_time": "12:00 AM EDT", "details_spans": "['pop culture & celebrities|', 'R|', '2 hrs 8 mins|', '2015|', 'UHD|']" }, { "date": "March 23", "name": "Yes Man", "airing_time": "11:07 PM EDT", "details_spans": "['comedy|', 'PG-13|', '1 hr 45 mins|', '2008|', 'HD|']" }, { "date": "March 23", "name": "The Verdict", "airing_time": "12:52 AM EDT", "details_spans": "['romantic dramas|', 'R|', '2 hrs 9 mins|', '1982|', 'HD|']" }, { "date": "March 23", "name": "The Vanishing of Sidney Hall", "airing_time": "3:01 AM EDT", "details_spans": "['drama|', '2 hrs 1 min|', '2018|']" }, { "date": "March 23", "name": "Killing Me Softly", "airing_time": "5:02 AM EDT", "details_spans": "['drama|', 'R|', '1 hr 41 mins|', '2003|', 'HD|']" }, { "date": "March 23", "name": "The Company You Keep", "airing_time": "6:43 AM EDT", "details_spans": "['suspense|', '2 hrs 2 mins|', '2013|']" }, { "date": "March 23", "name": "Don't Let Go", "airing_time": "8:45 AM EDT", "details_spans": "['horror|', '1 hr 44 mins|', '2019|']" }, { "date": "March 23", "name": "The Dead Don't Die", "airing_time": "10:29 AM EDT", "details_spans": "['comedy|', '1 hr 45 mins|', '2019|']" }, { "date": "March 23", "name": "Strange Days", "airing_time": "12:14 PM EDT", "details_spans": "['fantasy & sci-fi|', 'R|', '2 hrs 26 mins|', '1995|', 'HD|']" }, { "date": "March 23", "name": "She's Out of My League", "airing_time": "2:40 PM EDT", "details_spans": "['offbeat|', 'R|', '1 hr 45 mins|', '2010|', 'HD|']" }, { "date": "March 23", "name": "Let Me In", "airing_time": "4:25 PM EDT", "details_spans": "['mystery & suspense|', 'R|', '1 hr 56 mins|', '2010|', 'HD|']" }, { "date": "March 23", "name": "Baby Mama", "airing_time": "6:21 PM EDT", "details_spans": "['romantic comedy|', 'PG-13|', '1 hr 39 mins|', '2008|', 'HD|']" }, { "date": "March 23", "name": "Office Space", "airing_time": "8:00 PM EDT", "details_spans": "['cult classic|', 'R|', '1 hr 30 mins|', '1999|', 'HD|']" }, { "date": "March 23", "name": "Woman Walks Ahead", "airing_time": "9:30 PM EDT", "details_spans": "['drama|', '1 hr 43 mins|', '2018|']" }, { "date": "March 23", "name": "I Love You, Beth Cooper", "airing_time": "11:13 PM EDT", "details_spans": "['comedy|', '1 hr 42 mins|', '2009|']" }, { "date": "March 23", "name": "Elizabethtown", "airing_time": "12:55 AM EDT", "details_spans": "['comedy|', '2 hrs 4 mins|', '2005|']" } ] ================================================ FILE: cookbook/function_call_force_schema.py ================================================ from duckduckgo_search import DDGS import requests, os import json api_key=os.environ["GROQ_API_KEY"] header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally # proxy_url = "http://localhost:8000/proxy/groq/v1/chat/completions" def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] def duckduckgo_news(query, max_results=None): """ Use this function to get the latest news from DuckDuckGo. """ with DDGS() as ddgs: return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, "duckduckgo_news": duckduckgo_news, } request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", # "tool_choice": "auto", # "tool_choice": "none", "tool_choice": {"type": "function", "function": {"name": "duckduckgo_search"}}, "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } }, { "type": "function", "function": { "name": "duckduckgo_news", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } } ] } response = requests.post( proxy_url, headers=header, json=request ) # Check if the request was successful if response.status_code == 200: # Process the response data (if needed) res = response.json() message = res['choices'][0]['message'] tools_response_messages = [] if not message['content'] and 'tool_calls' in message: for tool_call in message['tool_calls']: tool_name = tool_call['function']['name'] tool_args = tool_call['function']['arguments'] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append({ "role": "tool", "content": json.dumps(tool_response) }) if tools_response_messages: request['messages'] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() print(res['choices'][0]['message']['content']) else: print("Error:", response.status_code, response.text) else: print(message['content']) else: print("Error:", response.status_code, response.text) ================================================ FILE: cookbook/function_call_force_tool_choice.py ================================================ from duckduckgo_search import DDGS import requests, os import json api_key=os.environ["GROQ_API_KEY"] header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } # proxy_url = "https://funckycall.ai/proxy/groq/v1/chat/completions" proxy_url = "http://localhost:8000/proxy/groq/v1/chat/completions" def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] def duckduckgo_news(query, max_results=None): """ Use this function to get the latest news from DuckDuckGo. """ with DDGS() as ddgs: return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, "duckduckgo_news": duckduckgo_news, } request = { "messages": [ { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, search in general and also search news, very short and concise.", } ], "model": "mixtral-8x7b-32768", # "tool_choice": "auto", # "tool_choice": None, "tool_choice": {"type": "function", "function": {"name": "duckduckgo_search"}}, "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": {"type": "string"}, "max_results": {"type": ["number", "null"]}, }, }, }, }, { "type": "function", "function": { "name": "duckduckgo_news", "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.", "parameters": { "type": "object", "properties": { "query": {"type": "string"}, "max_results": {"type": ["number", "null"]}, }, }, }, }, ], } response = requests.post( proxy_url, headers=header, json=request, ) # Check if the request was successful if response.status_code == 200: # Process the response data (if needed) res = response.json() message = res['choices'][0]['message'] tools_response_messages = [] if not message['content'] and 'tool_calls' in message: for tool_call in message['tool_calls']: tool_name = tool_call['function']['name'] tool_args = tool_call['function']['arguments'] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append({ "role": "tool", "content": json.dumps(tool_response) }) if tools_response_messages: request['messages'] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() print(res['choices'][0]['message']['content']) else: print("Error:", response.status_code, response.text) else: print(message['content']) else: print("Error:", response.status_code, response.text) ================================================ FILE: cookbook/function_call_ollama.py ================================================ from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo # 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 ;) my_ollama = OpenAILike( model="gemma:7b", api_key="", base_url="http://localhost:11235/proxy/ollama/v1" ) ollama_assistant = Assistant( llm=my_ollama, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) ollama_assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) ================================================ FILE: cookbook/function_call_phidata.py ================================================ from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo import os, json groq = OpenAILike( model="mixtral-8x7b-32768", api_key=os.environ["GROQ_API_KEY"], base_url="https://api.groq.com/openai/v1" ) assistant = Assistant( llm=groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) # If you run without a proxy, you will get a error, becuase Groq does not have a function to call # assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) my_groq = OpenAILike( model="mixtral-8x7b-32768", # or model="gemma-7b-it", api_key=os.environ["GROQ_API_KEY"], base_url="https://groqcall.ai/proxy/groq/v1" # or "http://localhost:8000/proxy/groq/v1" if running locally ) assistant = Assistant( llm=my_groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) ================================================ FILE: cookbook/function_call_vision.py ================================================ import requests, os api_key = os.environ["GROQ_API_KEY"] header = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally proxy_url = "http://localhost:8000/proxy/groq/v1/chat/completions" request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n", }, { "role": "user", "content": [ {"type": "text", "text": "What’s in this image?"}, { "type": "image_url", "image_url": { "url": "https://res.cloudinary.com/kidocode/image/upload/v1710690498/Gfp-wisconsin-madison-the-nature-boardwalk_m9jalr.jpg" }, }, ], }, { "role": "user", # "content": "What’s in this image?", "content": "Generate 3 keywords for the image description", }, ], "model": "mixtral-8x7b-32768" } response = requests.post(proxy_url, headers=header, json=request) response.text print(response.json()["choices"][0]["message"]["content"]) ================================================ FILE: cookbook/function_call_with_schema.py ================================================ from duckduckgo_search import DDGS import requests, os import json api_key=os.environ["GROQ_API_KEY"] header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] def duckduckgo_news(query, max_results=None): """ Use this function to get the latest news from DuckDuckGo. """ with DDGS() as ddgs: return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, "duckduckgo_news": duckduckgo_news, } request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } }, { "type": "function", "function": { "name": "duckduckgo_news", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } } ] } response = requests.post( proxy_url, headers=header, json=request ) # Check if the request was successful if response.status_code == 200: # Process the response data (if needed) res = response.json() message = res['choices'][0]['message'] tools_response_messages = [] if not message['content'] and 'tool_calls' in message: for tool_call in message['tool_calls']: tool_name = tool_call['function']['name'] tool_args = tool_call['function']['arguments'] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append({ "role": "tool", "content": json.dumps(tool_response) }) if tools_response_messages: request['messages'] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() print(res['choices'][0]['message']['content']) else: print("Error:", response.status_code, response.text) else: print(message['content']) else: print("Error:", response.status_code, response.text) ================================================ FILE: cookbook/function_call_without_schema.py ================================================ import requests import json import os api_key=os.environ["GROQ_API_KEY"], header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "What's happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduck.search" } }, { "type": "function", "function": { "name": "duckduck.news" } } ] } response = requests.post( proxy_url, headers=header, json=request ) print(response.json()["choices"][0]["message"]["content"]) ================================================ FILE: cookbook/functiona_call_groq_langchain.py ================================================ # pip install --upgrade --quiet langchain-groq tavily-python langchain langchainhub langchain-openai from langchain_core.prompts import ChatPromptTemplate from langchain_groq import ChatGroq import os from dotenv import load_dotenv load_dotenv() from langchain import hub from langchain.agents import create_openai_tools_agent from langchain_community.tools.tavily_search import TavilySearchResults, TavilyAnswer from langchain.agents import AgentExecutor # The following code raise an error. chat = ChatGroq( temperature=0, groq_api_key=os.environ["GROQ_API_KEY"], model_name="mixtral-8x7b-32768", ) prompt = hub.pull("hwchase17/openai-tools-agent") tools = [TavilySearchResults(max_results=1)] agent = create_openai_tools_agent(chat, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, stream_runnable = False) agent_executor.invoke({"input": "What is Langchain?"}) # The following code works fine using GroqCall (Funckycall) proxy chat = ChatGroq( temperature=0, groq_api_key=os.environ["GROQ_API_KEY"], model_name="mixtral-8x7b-32768", groq_api_base= "http://localhost:8000/proxy/groqchain" # groq_api_base= "http://groqcall.ai/proxy/groqchain" ) # Example 1: Chat with tools prompt = hub.pull("hwchase17/openai-tools-agent") tools = [TavilySearchResults(max_results=1)] agent = create_openai_tools_agent(chat, tools, prompt) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, stream_runnable = False) result = agent_executor.invoke({"input": "What is Langchain?"}) print(result) # Example 1: Simple chat system = "You are a helpful assistant." human = "{text}" prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)]) chain = prompt | chat result = chain.invoke({"text": "Explain the importance of low latency LLMs."}) print(result) ================================================ FILE: cookbook/resources.py ================================================ from phi.docker.app.postgres import PgVectorDb from phi.docker.resources import DockerResources # -*- PgVector2 running on port 5432:5432 vector_db = PgVectorDb( name="knowledge-db", pg_user="ai", pg_password="ai", pg_database="ai", host_port=5532, ) # -*- DockerResources dev_docker_resources = DockerResources(apps=[vector_db]) ================================================ FILE: examples/example_1.py ================================================ from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo import os, json groq = OpenAILike( model="mixtral-8x7b-32768", api_key=os.environ["GROQ_API_KEY"], base_url="https://api.groq.com/openai/v1" ) assistant = Assistant( llm=groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) my_groq = OpenAILike( # model="mixtral-8x7b-32768", model="gemma-7b-it", api_key=os.environ["GROQ_API_KEY"], base_url="https://groqcall.ai/proxy/groq/v1" ) assistant = Assistant( llm=my_groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) # 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 ;) # my_ollama = OpenAILike( # model="gemma:7b", # api_key="", # base_url="http://localhost:11235/proxy/ollama/v1" # ) # ollama_assistant = Assistant( # llm=my_ollama, # tools=[DuckDuckGo()], show_tool_calls=True, markdown=True # ) # ollama_assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False) ================================================ FILE: examples/example_2.py ================================================ from duckduckgo_search import DDGS import requests, os api_key=os.environ["GROQ_API_KEY"] import json header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] def duckduckgo_news(query, max_results=None): """ Use this function to get the latest news from DuckDuckGo. """ with DDGS() as ddgs: return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, "duckduckgo_news": duckduckgo_news, } request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } }, { "type": "function", "function": { "name": "duckduckgo_news", "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.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": [ "number", "null" ] } } } } } ] } response = requests.post( proxy_url, headers=header, json=request ) # Check if the request was successful if response.status_code == 200: # Process the response data (if needed) res = response.json() message = res['choices'][0]['message'] tools_response_messages = [] if not message['content'] and 'tool_calls' in message: for tool_call in message['tool_calls']: tool_name = tool_call['function']['name'] tool_args = tool_call['function']['arguments'] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append({ "role": "tool", "content": json.dumps(tool_response) }) if tools_response_messages: request['messages'] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request ) if response.status_code == 200: res = response.json() print(res['choices'][0]['message']['content']) else: print("Error:", response.status_code, response.text) else: print(message['content']) else: print("Error:", response.status_code, response.text) ================================================ FILE: examples/example_3.py ================================================ from duckduckgo_search import DDGS import requests, os api_key = os.environ["GROQ_API_KEY"] header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n", }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.", }, ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduck.search", }, }, { "type": "function", "function": { "name": "duckduck.news", }, }, ], } response = requests.post( proxy_url, headers=header, json=request, ) if response.status_code == 200: res = response.json() print(res["choices"][0]["message"]["content"]) else: print("Error:", response.status_code, response.text) ================================================ FILE: examples/example_4.py ================================================ from duckduckgo_search import DDGS import requests, os, json api_key = os.environ["GROQ_API_KEY"] header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" def duckduckgo_search(query, max_results=None): """ Use this function to search DuckDuckGo for a query. """ with DDGS() as ddgs: return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)] function_map = { "duckduckgo_search": duckduckgo_search, } request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n", }, { "role": "user", "content": "Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.", }, ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduckgo_search", "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.", "parameters": { "type": "object", "properties": { "query": {"type": "string"}, "max_results": {"type": ["number", "null"]}, }, }, }, }, { "type": "function", "function": { "name": "duckduck.news", }, }, ], } response = requests.post( proxy_url, headers= header, json=request, ) # Check if the request was successful if response.status_code == 200: # Process the response data (if needed) res = response.json() message = res["choices"][0]["message"] tools_response_messages = [] if not message["content"] and "tool_calls" in message: if 'resolved' in res: # Append resolved message to the tools response messages tools_response_messages.extend(res['resolved']) for tool_call in message["tool_calls"]: tool_name = tool_call["function"]["name"] tool_args = tool_call["function"]["arguments"] tool_args = json.loads(tool_args) if tool_name not in function_map: print(f"Error: {tool_name} is not a valid function name.") continue tool_func = function_map[tool_name] tool_response = tool_func(**tool_args) tools_response_messages.append( {"role": "tool", "content": json.dumps(tool_response), "name": tool_name, "tool_call_id": tool_call["id"]} ) if tools_response_messages: request["messages"] += tools_response_messages response = requests.post( proxy_url, headers=header, json=request, ) if response.status_code == 200: res = response.json() print(res["choices"][0]["message"]["content"]) else: print("Error:", response.status_code, response.text) else: print(message["content"]) else: print("Error:", response.status_code, response.text) ================================================ FILE: frontend/assets/README.md ================================================ # GroqCall.ai - Lightning-Fast LLM Function Calls [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1q3is7qynCsx4s7FBznCfTMnokbKWIv1F?usp=sharing) [![Version](https://img.shields.io/badge/version-0.0.1-blue.svg)](https://github.com/unclecode/groqcall) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) GroqCall 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. ## Quickstart ### Using the Pre-built Server To quickly start using GroqCall without running it locally, make requests to one of the following base URLs: - Cloud: `https://groqcall.ai/proxy/groq/v1` - Local: `http://localhost:8000` (if running the proxy server locally) ### Running the Proxy Locally 1. Clone the repository: ``` git clone https://github.com/unclecode/groqcall.git cd groqcall ``` 2. Create and activate a virtual environment: ``` python -m venv venv source venv/bin/activate ``` 3. Install dependencies: ``` pip install -r requirements.txt ``` 4. Run the FastAPI server: ``` ./venv/bin/uvicorn --app-dir app/ main:app --reload ``` ## Examples ### Using GroqCall with PhiData ```python from phi.llm.openai.like import OpenAILike from phi.assistant import Assistant from phi.tools.duckduckgo import DuckDuckGo my_groq = OpenAILike( model="mixtral-8x7b-32768", api_key="YOUR_GROQ_API_KEY", base_url="https://groqcall.ai/proxy/groq/v1" # or "http://localhost:8000/proxy/groq/v1" if running locally ) assistant = Assistant( llm=my_groq, tools=[DuckDuckGo()], show_tool_calls=True, markdown=True ) assistant.print_response("What's happening in France? Summarize top stories with sources, very short and concise.", stream=False) ``` ### Using GroqCall with Requests #### FuncHub: Schema-less Function Calls GroqCall introduces FuncHub, which allows you to make function calls without passing the function schema. ```python import requests api_key = "YOUR_GROQ_API_KEY" header = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions" # or "http://localhost:8000/proxy/groq/v1/chat/completions" if running locally request = { "messages": [ { "role": "system", "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n\n1. Use markdown to format your answers.\n" }, { "role": "user", "content": "What's happening in France? Summarize top stories with sources, very short and concise." } ], "model": "mixtral-8x7b-32768", "tool_choice": "auto", "tools": [ { "type": "function", "function": { "name": "duckduck.search" } }, { "type": "function", "function": { "name": "duckduck.news" } } ] } response = requests.post( proxy_url, headers=header, json=request ) print(response.json()["choices"][0]["message"]["content"]) ``` - 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. #### Passing Function Schemas If 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. #### Rune proxy with Ollama locally Function 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. ## Cookbook Explore the [Cookbook](https://github.com/unclecode/groqcall/tree/main/cookbook) for more examples and use cases of GroqCall. ## Motivation Groq 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. As 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. ## Contributing Contributions 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. Let's collaborate and make this repository even more awesome! 🚀 ## License This project is licensed under the Apache License 2.0. See [LICENSE](https://github.com/unclecode/groqcall/blob/main/LICENSE) for more information. ================================================ FILE: frontend/assets/markdown.css ================================================ @media (prefers-color-scheme: dark) { .markdown-body, [data-theme="dark"] { /*dark*/ color-scheme: dark; --color-prettylights-syntax-comment: #8b949e; --color-prettylights-syntax-constant: #79c0ff; --color-prettylights-syntax-entity: #d2a8ff; --color-prettylights-syntax-storage-modifier-import: #c9d1d9; --color-prettylights-syntax-entity-tag: #7ee787; --color-prettylights-syntax-keyword: #ff7b72; --color-prettylights-syntax-string: #a5d6ff; --color-prettylights-syntax-variable: #ffa657; --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; --color-prettylights-syntax-invalid-illegal-bg: #8e1519; --color-prettylights-syntax-carriage-return-text: #f0f6fc; --color-prettylights-syntax-carriage-return-bg: #b62324; --color-prettylights-syntax-string-regexp: #7ee787; --color-prettylights-syntax-markup-list: #f2cc60; --color-prettylights-syntax-markup-heading: #1f6feb; --color-prettylights-syntax-markup-italic: #c9d1d9; --color-prettylights-syntax-markup-bold: #c9d1d9; --color-prettylights-syntax-markup-deleted-text: #ffdcd7; --color-prettylights-syntax-markup-deleted-bg: #67060c; --color-prettylights-syntax-markup-inserted-text: #aff5b4; --color-prettylights-syntax-markup-inserted-bg: #033a16; --color-prettylights-syntax-markup-changed-text: #ffdfb6; --color-prettylights-syntax-markup-changed-bg: #5a1e02; --color-prettylights-syntax-markup-ignored-text: #c9d1d9; --color-prettylights-syntax-markup-ignored-bg: #1158c7; --color-prettylights-syntax-meta-diff-range: #d2a8ff; --color-prettylights-syntax-brackethighlighter-angle: #8b949e; --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; --color-fg-default: #e6edf3; --color-fg-muted: #848d97; --color-fg-subtle: #6e7681; --color-canvas-default: #0d1117; --color-canvas-subtle: #161b22; --color-border-default: #30363d; --color-border-muted: #21262d; --color-neutral-muted: rgba(110,118,129,0.4); --color-accent-fg: #2f81f7; --color-accent-emphasis: #1f6feb; --color-success-fg: #3fb950; --color-success-emphasis: #238636; --color-attention-fg: #d29922; --color-attention-emphasis: #9e6a03; --color-attention-subtle: rgba(187,128,9,0.15); --color-danger-fg: #f85149; --color-danger-emphasis: #da3633; --color-done-fg: #a371f7; --color-done-emphasis: #8957e5; } } @media (prefers-color-scheme: light) { .markdown-body, [data-theme="light"] { /*light*/ color-scheme: light; --color-prettylights-syntax-comment: #57606a; --color-prettylights-syntax-constant: #0550ae; --color-prettylights-syntax-entity: #6639ba; --color-prettylights-syntax-storage-modifier-import: #24292f; --color-prettylights-syntax-entity-tag: #116329; --color-prettylights-syntax-keyword: #cf222e; --color-prettylights-syntax-string: #0a3069; --color-prettylights-syntax-variable: #953800; --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; --color-prettylights-syntax-invalid-illegal-bg: #82071e; --color-prettylights-syntax-carriage-return-text: #f6f8fa; --color-prettylights-syntax-carriage-return-bg: #cf222e; --color-prettylights-syntax-string-regexp: #116329; --color-prettylights-syntax-markup-list: #3b2300; --color-prettylights-syntax-markup-heading: #0550ae; --color-prettylights-syntax-markup-italic: #24292f; --color-prettylights-syntax-markup-bold: #24292f; --color-prettylights-syntax-markup-deleted-text: #82071e; --color-prettylights-syntax-markup-deleted-bg: #ffebe9; --color-prettylights-syntax-markup-inserted-text: #116329; --color-prettylights-syntax-markup-inserted-bg: #dafbe1; --color-prettylights-syntax-markup-changed-text: #953800; --color-prettylights-syntax-markup-changed-bg: #ffd8b5; --color-prettylights-syntax-markup-ignored-text: #eaeef2; --color-prettylights-syntax-markup-ignored-bg: #0550ae; --color-prettylights-syntax-meta-diff-range: #8250df; --color-prettylights-syntax-brackethighlighter-angle: #57606a; --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; --color-prettylights-syntax-constant-other-reference-link: #0a3069; --color-fg-default: #1F2328; --color-fg-muted: #656d76; --color-fg-subtle: #6e7781; --color-canvas-default: #ffffff; --color-canvas-subtle: #f6f8fa; --color-border-default: #d0d7de; --color-border-muted: hsla(210,18%,87%,1); --color-neutral-muted: rgba(175,184,193,0.2); --color-accent-fg: #0969da; --color-accent-emphasis: #0969da; --color-success-fg: #1a7f37; --color-success-emphasis: #1f883d; --color-attention-fg: #9a6700; --color-attention-emphasis: #9a6700; --color-attention-subtle: #fff8c5; --color-danger-fg: #d1242f; --color-danger-emphasis: #cf222e; --color-done-fg: #8250df; --color-done-emphasis: #8250df; } } .markdown-body { -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; margin: 0; color: var(--color-fg-default); background-color: var(--color-canvas-default); font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; font-size: 16px; line-height: 1.5; word-wrap: break-word; } .markdown-body .octicon { display: inline-block; fill: currentColor; vertical-align: text-bottom; } .markdown-body h1:hover .anchor .octicon-link:before, .markdown-body h2:hover .anchor .octicon-link:before, .markdown-body h3:hover .anchor .octicon-link:before, .markdown-body h4:hover .anchor .octicon-link:before, .markdown-body h5:hover .anchor .octicon-link:before, .markdown-body h6:hover .anchor .octicon-link:before { width: 16px; height: 16px; content: ' '; display: inline-block; background-color: currentColor; -webkit-mask-image: url("data:image/svg+xml,"); mask-image: url("data:image/svg+xml,"); } .markdown-body details, .markdown-body figcaption, .markdown-body figure { display: block; } .markdown-body summary { display: list-item; } .markdown-body [hidden] { display: none !important; } .markdown-body a { background-color: transparent; color: var(--color-accent-fg); text-decoration: none; } .markdown-body abbr[title] { border-bottom: none; -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } .markdown-body b, .markdown-body strong { font-weight: var(--base-text-weight-semibold, 600); } .markdown-body dfn { font-style: italic; } .markdown-body h1 { margin: .67em 0; font-weight: var(--base-text-weight-semibold, 600); padding-bottom: .3em; font-size: 2em; border-bottom: 1px solid var(--color-border-muted); } .markdown-body mark { background-color: var(--color-attention-subtle); color: var(--color-fg-default); } .markdown-body small { font-size: 90%; } .markdown-body sub, .markdown-body sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } .markdown-body sub { bottom: -0.25em; } .markdown-body sup { top: -0.5em; } .markdown-body img { border-style: none; max-width: 100%; box-sizing: content-box; background-color: var(--color-canvas-default); } .markdown-body code, .markdown-body kbd, .markdown-body pre, .markdown-body samp { font-family: monospace; font-size: 1em; } .markdown-body figure { margin: 1em 40px; } .markdown-body hr { box-sizing: content-box; overflow: hidden; background: transparent; border-bottom: 1px solid var(--color-border-muted); height: .25em; padding: 0; margin: 24px 0; background-color: var(--color-border-default); border: 0; } .markdown-body input { font: inherit; margin: 0; overflow: visible; font-family: inherit; font-size: inherit; line-height: inherit; } .markdown-body [type=button], .markdown-body [type=reset], .markdown-body [type=submit] { -webkit-appearance: button; appearance: button; } .markdown-body [type=checkbox], .markdown-body [type=radio] { box-sizing: border-box; padding: 0; } .markdown-body [type=number]::-webkit-inner-spin-button, .markdown-body [type=number]::-webkit-outer-spin-button { height: auto; } .markdown-body [type=search]::-webkit-search-cancel-button, .markdown-body [type=search]::-webkit-search-decoration { -webkit-appearance: none; appearance: none; } .markdown-body ::-webkit-input-placeholder { color: inherit; opacity: .54; } .markdown-body ::-webkit-file-upload-button { -webkit-appearance: button; appearance: button; font: inherit; } .markdown-body a:hover { text-decoration: underline; } .markdown-body ::placeholder { color: var(--color-fg-subtle); opacity: 1; } .markdown-body hr::before { display: table; content: ""; } .markdown-body hr::after { display: table; clear: both; content: ""; } .markdown-body table { border-spacing: 0; border-collapse: collapse; display: block; width: max-content; max-width: 100%; overflow: auto; } .markdown-body td, .markdown-body th { padding: 0; } .markdown-body details summary { cursor: pointer; } .markdown-body details:not([open])>*:not(summary) { display: none !important; } .markdown-body a:focus, .markdown-body [role=button]:focus, .markdown-body input[type=radio]:focus, .markdown-body input[type=checkbox]:focus { outline: 2px solid var(--color-accent-fg); outline-offset: -2px; box-shadow: none; } .markdown-body a:focus:not(:focus-visible), .markdown-body [role=button]:focus:not(:focus-visible), .markdown-body input[type=radio]:focus:not(:focus-visible), .markdown-body input[type=checkbox]:focus:not(:focus-visible) { outline: solid 1px transparent; } .markdown-body a:focus-visible, .markdown-body [role=button]:focus-visible, .markdown-body input[type=radio]:focus-visible, .markdown-body input[type=checkbox]:focus-visible { outline: 2px solid var(--color-accent-fg); outline-offset: -2px; box-shadow: none; } .markdown-body a:not([class]):focus, .markdown-body a:not([class]):focus-visible, .markdown-body input[type=radio]:focus, .markdown-body input[type=radio]:focus-visible, .markdown-body input[type=checkbox]:focus, .markdown-body input[type=checkbox]:focus-visible { outline-offset: 0; } .markdown-body kbd { display: inline-block; padding: 3px 5px; font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; line-height: 10px; color: var(--color-fg-default); vertical-align: middle; background-color: var(--color-canvas-subtle); border: solid 1px var(--color-neutral-muted); border-bottom-color: var(--color-neutral-muted); border-radius: 6px; box-shadow: inset 0 -1px 0 var(--color-neutral-muted); } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { margin-top: 24px; margin-bottom: 16px; font-weight: var(--base-text-weight-semibold, 600); line-height: 1.25; } .markdown-body h2 { font-weight: var(--base-text-weight-semibold, 600); padding-bottom: .3em; font-size: 1.5em; border-bottom: 1px solid var(--color-border-muted); } .markdown-body h3 { font-weight: var(--base-text-weight-semibold, 600); font-size: 1.25em; } .markdown-body h4 { font-weight: var(--base-text-weight-semibold, 600); font-size: 1em; } .markdown-body h5 { font-weight: var(--base-text-weight-semibold, 600); font-size: .875em; } .markdown-body h6 { font-weight: var(--base-text-weight-semibold, 600); font-size: .85em; color: var(--color-fg-muted); } .markdown-body p { margin-top: 0; margin-bottom: 10px; } .markdown-body blockquote { margin: 0; padding: 0 1em; color: var(--color-fg-muted); border-left: .25em solid var(--color-border-default); } .markdown-body ul, .markdown-body ol { margin-top: 0; margin-bottom: 0; padding-left: 2em; } .markdown-body ol ol, .markdown-body ul ol { list-style-type: lower-roman; } .markdown-body ul ul ol, .markdown-body ul ol ol, .markdown-body ol ul ol, .markdown-body ol ol ol { list-style-type: lower-alpha; } .markdown-body dd { margin-left: 0; } .markdown-body tt, .markdown-body code, .markdown-body samp { font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; font-size: 12px; } .markdown-body pre { margin-top: 0; margin-bottom: 0; font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; font-size: 12px; word-wrap: normal; } .markdown-body .octicon { display: inline-block; overflow: visible !important; vertical-align: text-bottom; fill: currentColor; } .markdown-body input::-webkit-outer-spin-button, .markdown-body input::-webkit-inner-spin-button { margin: 0; -webkit-appearance: none; appearance: none; } .markdown-body .mr-2 { margin-right: var(--base-size-8, 8px) !important; } .markdown-body::before { display: table; content: ""; } .markdown-body::after { display: table; clear: both; content: ""; } .markdown-body>*:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !important; } .markdown-body a:not([href]) { color: inherit; text-decoration: none; } .markdown-body .absent { color: var(--color-danger-fg); } .markdown-body .anchor { float: left; padding-right: 4px; margin-left: -20px; line-height: 1; } .markdown-body .anchor:focus { outline: none; } .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre, .markdown-body details { margin-top: 0; margin-bottom: 16px; } .markdown-body blockquote>:first-child { margin-top: 0; } .markdown-body blockquote>:last-child { margin-bottom: 0; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: var(--color-fg-default); vertical-align: middle; visibility: hidden; } .markdown-body h1:hover .anchor, .markdown-body h2:hover .anchor, .markdown-body h3:hover .anchor, .markdown-body h4:hover .anchor, .markdown-body h5:hover .anchor, .markdown-body h6:hover .anchor { text-decoration: none; } .markdown-body h1:hover .anchor .octicon-link, .markdown-body h2:hover .anchor .octicon-link, .markdown-body h3:hover .anchor .octicon-link, .markdown-body h4:hover .anchor .octicon-link, .markdown-body h5:hover .anchor .octicon-link, .markdown-body h6:hover .anchor .octicon-link { visibility: visible; } .markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code { padding: 0 .2em; font-size: inherit; } .markdown-body summary h1, .markdown-body summary h2, .markdown-body summary h3, .markdown-body summary h4, .markdown-body summary h5, .markdown-body summary h6 { display: inline-block; } .markdown-body summary h1 .anchor, .markdown-body summary h2 .anchor, .markdown-body summary h3 .anchor, .markdown-body summary h4 .anchor, .markdown-body summary h5 .anchor, .markdown-body summary h6 .anchor { margin-left: -40px; } .markdown-body summary h1, .markdown-body summary h2 { padding-bottom: 0; border-bottom: 0; } .markdown-body ul.no-list, .markdown-body ol.no-list { padding: 0; list-style-type: none; } .markdown-body ol[type="a s"] { list-style-type: lower-alpha; } .markdown-body ol[type="A s"] { list-style-type: upper-alpha; } .markdown-body ol[type="i s"] { list-style-type: lower-roman; } .markdown-body ol[type="I s"] { list-style-type: upper-roman; } .markdown-body ol[type="1"] { list-style-type: decimal; } .markdown-body div>ol:not([type]) { list-style-type: decimal; } .markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul { margin-top: 0; margin-bottom: 0; } .markdown-body li>p { margin-top: 16px; } .markdown-body li+li { margin-top: .25em; } .markdown-body dl { padding: 0; } .markdown-body dl dt { padding: 0; margin-top: 16px; font-size: 1em; font-style: italic; font-weight: var(--base-text-weight-semibold, 600); } .markdown-body dl dd { padding: 0 16px; margin-bottom: 16px; } .markdown-body table th { font-weight: var(--base-text-weight-semibold, 600); } .markdown-body table th, .markdown-body table td { padding: 6px 13px; border: 1px solid var(--color-border-default); } .markdown-body table td>:last-child { margin-bottom: 0; } .markdown-body table tr { background-color: var(--color-canvas-default); border-top: 1px solid var(--color-border-muted); } .markdown-body table tr:nth-child(2n) { background-color: var(--color-canvas-subtle); } .markdown-body table img { background-color: transparent; } .markdown-body img[align=right] { padding-left: 20px; } .markdown-body img[align=left] { padding-right: 20px; } .markdown-body .emoji { max-width: none; vertical-align: text-top; background-color: transparent; } .markdown-body span.frame { display: block; overflow: hidden; } .markdown-body span.frame>span { display: block; float: left; width: auto; padding: 7px; margin: 13px 0 0; overflow: hidden; border: 1px solid var(--color-border-default); } .markdown-body span.frame span img { display: block; float: left; } .markdown-body span.frame span span { display: block; padding: 5px 0 0; clear: both; color: var(--color-fg-default); } .markdown-body span.align-center { display: block; overflow: hidden; clear: both; } .markdown-body span.align-center>span { display: block; margin: 13px auto 0; overflow: hidden; text-align: center; } .markdown-body span.align-center span img { margin: 0 auto; text-align: center; } .markdown-body span.align-right { display: block; overflow: hidden; clear: both; } .markdown-body span.align-right>span { display: block; margin: 13px 0 0; overflow: hidden; text-align: right; } .markdown-body span.align-right span img { margin: 0; text-align: right; } .markdown-body span.float-left { display: block; float: left; margin-right: 13px; overflow: hidden; } .markdown-body span.float-left span { margin: 13px 0 0; } .markdown-body span.float-right { display: block; float: right; margin-left: 13px; overflow: hidden; } .markdown-body span.float-right>span { display: block; margin: 13px auto 0; overflow: hidden; text-align: right; } .markdown-body code, .markdown-body tt { padding: .2em .4em; margin: 0; font-size: 85%; white-space: break-spaces; background-color: var(--color-neutral-muted); border-radius: 6px; } .markdown-body code br, .markdown-body tt br { display: none; } .markdown-body del code { text-decoration: inherit; } .markdown-body samp { font-size: 85%; } .markdown-body pre code { font-size: 100%; } .markdown-body pre>code { padding: 0; margin: 0; word-break: normal; white-space: pre; background: transparent; border: 0; } .markdown-body .highlight { margin-bottom: 16px; } .markdown-body .highlight pre { margin-bottom: 0; word-break: normal; } .markdown-body .highlight pre, .markdown-body pre { padding: 16px; overflow: auto; font-size: 85%; line-height: 1.45; color: var(--color-fg-default); background-color: var(--color-canvas-subtle); border-radius: 6px; } .markdown-body pre code, .markdown-body pre tt { display: inline; max-width: auto; padding: 0; margin: 0; overflow: visible; line-height: inherit; word-wrap: normal; background-color: transparent; border: 0; } .markdown-body .csv-data td, .markdown-body .csv-data th { padding: 5px; overflow: hidden; font-size: 12px; line-height: 1; text-align: left; white-space: nowrap; } .markdown-body .csv-data .blob-num { padding: 10px 8px 9px; text-align: right; background: var(--color-canvas-default); border: 0; } .markdown-body .csv-data tr { border-top: 0; } .markdown-body .csv-data th { font-weight: var(--base-text-weight-semibold, 600); background: var(--color-canvas-subtle); border-top: 0; } .markdown-body [data-footnote-ref]::before { content: "["; } .markdown-body [data-footnote-ref]::after { content: "]"; } .markdown-body .footnotes { font-size: 12px; color: var(--color-fg-muted); border-top: 1px solid var(--color-border-default); } .markdown-body .footnotes ol { padding-left: 16px; } .markdown-body .footnotes ol ul { display: inline-block; padding-left: 16px; margin-top: 16px; } .markdown-body .footnotes li { position: relative; } .markdown-body .footnotes li:target::before { position: absolute; top: -8px; right: -8px; bottom: -8px; left: -24px; pointer-events: none; content: ""; border: 2px solid var(--color-accent-emphasis); border-radius: 6px; } .markdown-body .footnotes li:target { color: var(--color-fg-default); } .markdown-body .footnotes .data-footnote-backref g-emoji { font-family: monospace; } .markdown-body .pl-c { color: var(--color-prettylights-syntax-comment); } .markdown-body .pl-c1, .markdown-body .pl-s .pl-v { color: var(--color-prettylights-syntax-constant); } .markdown-body .pl-e, .markdown-body .pl-en { color: var(--color-prettylights-syntax-entity); } .markdown-body .pl-smi, .markdown-body .pl-s .pl-s1 { color: var(--color-prettylights-syntax-storage-modifier-import); } .markdown-body .pl-ent { color: var(--color-prettylights-syntax-entity-tag); } .markdown-body .pl-k { color: var(--color-prettylights-syntax-keyword); } .markdown-body .pl-s, .markdown-body .pl-pds, .markdown-body .pl-s .pl-pse .pl-s1, .markdown-body .pl-sr, .markdown-body .pl-sr .pl-cce, .markdown-body .pl-sr .pl-sre, .markdown-body .pl-sr .pl-sra { color: var(--color-prettylights-syntax-string); } .markdown-body .pl-v, .markdown-body .pl-smw { color: var(--color-prettylights-syntax-variable); } .markdown-body .pl-bu { color: var(--color-prettylights-syntax-brackethighlighter-unmatched); } .markdown-body .pl-ii { color: var(--color-prettylights-syntax-invalid-illegal-text); background-color: var(--color-prettylights-syntax-invalid-illegal-bg); } .markdown-body .pl-c2 { color: var(--color-prettylights-syntax-carriage-return-text); background-color: var(--color-prettylights-syntax-carriage-return-bg); } .markdown-body .pl-sr .pl-cce { font-weight: bold; color: var(--color-prettylights-syntax-string-regexp); } .markdown-body .pl-ml { color: var(--color-prettylights-syntax-markup-list); } .markdown-body .pl-mh, .markdown-body .pl-mh .pl-en, .markdown-body .pl-ms { font-weight: bold; color: var(--color-prettylights-syntax-markup-heading); } .markdown-body .pl-mi { font-style: italic; color: var(--color-prettylights-syntax-markup-italic); } .markdown-body .pl-mb { font-weight: bold; color: var(--color-prettylights-syntax-markup-bold); } .markdown-body .pl-md { color: var(--color-prettylights-syntax-markup-deleted-text); background-color: var(--color-prettylights-syntax-markup-deleted-bg); } .markdown-body .pl-mi1 { color: var(--color-prettylights-syntax-markup-inserted-text); background-color: var(--color-prettylights-syntax-markup-inserted-bg); } .markdown-body .pl-mc { color: var(--color-prettylights-syntax-markup-changed-text); background-color: var(--color-prettylights-syntax-markup-changed-bg); } .markdown-body .pl-mi2 { color: var(--color-prettylights-syntax-markup-ignored-text); background-color: var(--color-prettylights-syntax-markup-ignored-bg); } .markdown-body .pl-mdr { font-weight: bold; color: var(--color-prettylights-syntax-meta-diff-range); } .markdown-body .pl-ba { color: var(--color-prettylights-syntax-brackethighlighter-angle); } .markdown-body .pl-sg { color: var(--color-prettylights-syntax-sublimelinter-gutter-mark); } .markdown-body .pl-corl { text-decoration: underline; color: var(--color-prettylights-syntax-constant-other-reference-link); } .markdown-body g-emoji { display: inline-block; min-width: 1ch; font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; font-size: 1em; font-style: normal !important; font-weight: var(--base-text-weight-normal, 400); line-height: 1; vertical-align: -0.075em; } .markdown-body g-emoji img { width: 1em; height: 1em; } .markdown-body .task-list-item { list-style-type: none; } .markdown-body .task-list-item label { font-weight: var(--base-text-weight-normal, 400); } .markdown-body .task-list-item.enabled label { cursor: pointer; } .markdown-body .task-list-item+.task-list-item { margin-top: 4px; } .markdown-body .task-list-item .handle { display: none; } .markdown-body .task-list-item-checkbox { margin: 0 .2em .25em -1.4em; vertical-align: middle; } .markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox { margin: 0 -1.6em .25em .2em; } .markdown-body .contains-task-list { position: relative; } .markdown-body .contains-task-list:hover .task-list-item-convert-container, .markdown-body .contains-task-list:focus-within .task-list-item-convert-container { display: block; width: auto; height: 24px; overflow: visible; clip: auto; } .markdown-body ::-webkit-calendar-picker-indicator { filter: invert(50%); } .markdown-body .markdown-alert { padding: var(--base-size-8) var(--base-size-16); margin-bottom: 16px; color: inherit; border-left: .25em solid var(--color-border-default); } .markdown-body .markdown-alert>:first-child { margin-top: 0; } .markdown-body .markdown-alert>:last-child { margin-bottom: 0; } .markdown-body .markdown-alert .markdown-alert-title { display: flex; font-weight: var(--base-text-weight-medium, 500); align-items: center; line-height: 1; } .markdown-body .markdown-alert.markdown-alert-note { border-left-color: var(--color-accent-emphasis); } .markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { color: var(--color-accent-fg); } .markdown-body .markdown-alert.markdown-alert-important { border-left-color: var(--color-done-emphasis); } .markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { color: var(--color-done-fg); } .markdown-body .markdown-alert.markdown-alert-warning { border-left-color: var(--color-attention-emphasis); } .markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { color: var(--color-attention-fg); } .markdown-body .markdown-alert.markdown-alert-tip { border-left-color: var(--color-success-emphasis); } .markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { color: var(--color-success-fg); } .markdown-body .markdown-alert.markdown-alert-caution { border-left-color: var(--color-danger-emphasis); } .markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { color: var(--color-danger-fg); } ================================================ FILE: frontend/assets/style.css ================================================ @import "tailwindcss/base"; @import "tailwindcss/components"; @import "tailwindcss/utilities"; ================================================ FILE: frontend/pages/index.html ================================================ GroqCall
Loading
================================================ FILE: frontend/pages/index_old.html ================================================ GroqCall

GroqCall.ai

Open In Colab Version License: MIT

GroqCall is a proxy server that provides function calls 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 execute them through multiple calls.

Check github repo for more info: https://github.com/unclecode/groqcall

Motivation 🚀

Groq 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;

I 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.

Unable 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.

Groq Chip Powered by Groq

Running the Proxy Locally 🖥️

To run this proxy locally on your own machine, follow these steps:

  1. Clone the GitHub repository:
  2. git clone https://github.com/unclecode/groqcall.git
  3. Navigate to the project directory:
  4. cd groqcall
  5. Create a virtual environment:
  6. python -m venv venv
  7. Activate the virtual environment:
  8. source venv/bin/activate
  9. Install the required libraries:
  10. pip install -r requirements.txt
  11. Run the FastAPI server:
  12. .venv/bin/uvicorn --app-dir app/ main:app --reload

Using the Pre-built Server 🌐

For 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.

To use the pre-built server, simply make requests to the following base URL: https://groqcall.ai/proxy/groq/v1

Exploring GroqCall.ai 🚀

This README is organized into three main sections, each showcasing different aspects of GroqCall.ai:

  • 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.
  • FunckyHub: The third section introduces the concept of FunckyHub, a useful feature that simplifies the process of executing functions. With FunckyHub, 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 calls in a production server.
  • 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.
# The following libraries are optional if you're interested in using PhiData or managing your tools on the client side.
    !pip install phidata > /dev/null
    !pip install openai > /dev/null
    !pip install duckduckgo-search > /dev/null
    

Sending POST request, with full functions implementation

Check out the example file example_2.py for a full implementation of the following code.

from duckduckgo_search import DDGS
    import requests, os
    import json
    
    # Here you pass your own GROQ API key
    api_key=userdata.get("GROQ_API_KEY")
    header = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions"
    
    
    def duckduckgo_search(query, max_results=None):
        """
        Use this function to search DuckDuckGo for a query.
        """
        with DDGS() as ddgs:
            return [r for r in ddgs.text(query, safesearch='off', max_results=max_results)]
    
    def duckduckgo_news(query, max_results=None):
        """
        Use this function to get the latest news from DuckDuckGo.
        """
        with DDGS() as ddgs:
            return [r for r in ddgs.news(query, safesearch='off', max_results=max_results)]
    
    function_map = {
        "duckduckgo_search": duckduckgo_search,
        "duckduckgo_news": duckduckgo_news,
    }
    
    request = {
        "messages": [
            {
                "role": "system",
                "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n<instructions>\n1. Use markdown to format your answers.\n</instructions>"
            },
            {
                "role": "user",
                "content": "Whats happening in France? Summarize top stories with sources, very short and concise."
            }
        ],
        "model": "mixtral-8x7b-32768",
        "tool_choice": "auto",
        "tools": [
            {
                "type": "function",
                "function": {
                    "name": "duckduckgo_search",
                    "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.",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "query": {
                                "type": "string"
                            },
                            "max_results": {
                                "type": [
                                    "number",
                                    "null"
                                ]
                            }
                        }
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "duckduckgo_news",
                    "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.",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "query": {
                                "type": "string"
                            },
                            "max_results": {
                                "type": [
                                    "number",
                                    "null"
                                ]
                            }
                        }
                    }
                }
            }
        ]
    }
    
    response = requests.post(
        proxy_url,
        headers=header,
        json=request
    )
    if response.status_code == 200:
        res = response.json()
        message = res['choices'][0]['message']
        tools_response_messages = []
        if not message['content'] and 'tool_calls' in message:
            for tool_call in message['tool_calls']:
                tool_name = tool_call['function']['name']
                tool_args = tool_call['function']['arguments']
                tool_args = json.loads(tool_args)
                if tool_name not in function_map:
                    print(f"Error: {tool_name} is not a valid function name.")
                    continue
                tool_func = function_map[tool_name]
                tool_response = tool_func(**tool_args)
                tools_response_messages.append({
                    "role": "tool", "content": json.dumps(tool_response)
                })
    
            if tools_response_messages:
                request['messages'] += tools_response_messages
                response = requests.post(
                    proxy_url,
                    headers=header,
                    json=request
                )
                if response.status_code == 200:
                    res = response.json()
                    print(res['choices'][0]['message']['content'])
                else:
                    print("Error:", response.status_code, response.text)
        else:
            print(message['content'])
    else:
        print("Error:", response.status_code, response.text)
    

Schema-less Function Call 🤩

Check out the example file example_3.py for a full implementation of the following code.

In 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.

from duckduckgo_search import DDGS
    import requests, os
    api_key = userdata.get("GROQ_API_KEY")
    header = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    proxy_url = "https://groqcall.ai/proxy/groq/v1/chat/completions"
    
    
    request = {
        "messages": [
            {
                "role": "system",
                "content": "YOU MUST FOLLOW THESE INSTRUCTIONS CAREFULLY.\n<instructions>\n1. Use markdown to format your answers.\n</instructions>",
            },
            {
                "role": "user",
                "content": "Whats happening in France? Summarize top stories with sources, very short and concise. Also please search about the histoy of france as well.",
            },
        ],
        "model": "mixtral-8x7b-32768",
        "tool_choice": "auto",
        "tools": [
            {
                "type": "function",
                "function": {
                    "name": "duckduck.search",
                },
            },
            {
                "type": "function",
                "function": {
                    "name": "duckduck.news",
                },
            },
        ],
    }
    
    response = requests.post(
        proxy_url,
        headers=header,
        json=request,
    )
    
    if response.status_code == 200:
        res = response.json()
        print(res["choices"][0]["message"]["content"])
    else:
        print("Error:", response.status_code, response.text)
    

Using with PhiData

Check out the example file example_1.py for a full implementation of the following code.

PhiData 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.

from google.README import userdata
    from phi.llm.openai.like import OpenAILike
    from phi.assistant import Assistant
    from phi.tools.duckduckgo import DuckDuckGo
    import os, json
    
    
    my_groq = OpenAILike(
            model="mixtral-8x7b-32768",
            api_key=userdata.get("GROQ_API_KEY"),
            base_url="https://groqcall.ai/proxy/groq/v1"
        )
    assistant = Assistant(
        llm=my_groq,
        tools=[DuckDuckGo()], show_tool_calls=True, markdown=True
    )
    assistant.print_response("Whats happening in France? Summarize top stories with sources, very short and concise.", stream=False)
    
    

Contributions Welcome! 🙌

I 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.

Here's how you can get involved:

  1. Fork the repository and create your own branch.
  2. Implement new functions, integrate additional services, or make improvements to the existing codebase.
  3. Test your changes to ensure they work as expected.
  4. Submit a pull request describing the changes you have made and why they are valuable.

If 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:

I'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!

Together, let's make this repository even more awesome! 🚀

================================================ FILE: requirements.txt ================================================ aiohttp==3.9.3 aiosignal==1.3.1 annotated-types==0.6.0 anyio==4.3.0 appnope==0.1.4 asttokens==2.4.1 async-timeout==4.0.3 attrs==23.2.0 boto3==1.34.55 botocore==1.34.55 certifi==2024.2.2 cffi==1.16.0 charset-normalizer==3.3.2 click==8.1.7 comm==0.2.1 curl_cffi==0.6.2 debugpy==1.8.1 decorator==5.1.1 distro==1.9.0 docker==7.0.0 duckduckgo_search==4.5.0 exceptiongroup==1.2.0 executing==2.0.1 fastapi==0.110.0 filelock==3.13.1 frozenlist==1.4.1 fsspec==2024.2.0 gitdb==4.0.11 GitPython==3.1.42 groq==0.4.2 h11==0.14.0 httpcore==1.0.4 httpx==0.25.2 huggingface-hub==0.21.3 idna==3.6 importlib-metadata==7.0.1 ipykernel==6.29.3 ipython==8.22.2 jedi==0.19.1 Jinja2==3.1.3 jmespath==1.0.1 jupyter_client==8.6.0 jupyter_core==5.7.1 litellm==1.31.8 lxml==5.1.0 markdown-it-py==3.0.0 MarkupSafe==2.1.5 matplotlib-inline==0.1.6 mdurl==0.1.2 mistralai==0.1.3 multidict==6.0.5 nest-asyncio==1.6.0 numpy==1.26.4 ollama==0.1.7 openai==1.13.3 orjson==3.9.15 packaging==23.2 pandas==2.2.1 parso==0.8.3 pexpect==4.9.0 phidata==2.3.50 platformdirs==4.2.0 prompt-toolkit==3.0.43 psutil==5.9.8 ptyprocess==0.7.0 pure-eval==0.2.2 pyarrow==15.0.0 pycparser==2.21 pydantic==2.6.3 pydantic-settings==2.2.1 pydantic_core==2.16.3 Pygments==2.17.2 python-dateutil==2.9.0.post0 python-dotenv==1.0.1 pytz==2024.1 PyYAML==6.0.1 pyzmq==25.1.2 regex==2023.12.25 replicate==0.24.0 requests==2.31.0 rich==13.7.1 s3transfer==0.10.0 six==1.16.0 smmap==5.0.1 sniffio==1.3.1 stack-data==0.6.3 starlette==0.36.3 tiktoken==0.6.0 tokenizers==0.15.2 tomli==2.0.1 tornado==6.4 tqdm==4.66.2 traitlets==5.14.1 typer==0.9.0 typing_extensions==4.10.0 tzdata==2024.1 urllib3==2.0.7 uvicorn==0.27.1 wcwidth==0.2.13 yarl==1.9.4 zipp==3.17.0