From 0172d938b4bf1e9da9f3b796dbfce64c565ce565 Mon Sep 17 00:00:00 2001 From: ccurme Date: Thu, 31 Oct 2024 12:37:41 -0400 Subject: [PATCH] community: add AzureOpenAIWhisperParser (#27796) Commandeered from https://github.com/langchain-ai/langchain/pull/26757. --------- Co-authored-by: Sheepsta300 <128811766+Sheepsta300@users.noreply.github.com> --- .../parsers/azure_openai_whisper_parser.ipynb | 192 +++++++++++++++ .../document_loaders/parsers/audio.py | 227 +++++++++++++++++- libs/community/tests/examples/hello_world.m4a | Bin 0 -> 61634 bytes .../parsers/test_azure_whisper_parser.py | 104 ++++++++ 4 files changed, 513 insertions(+), 10 deletions(-) create mode 100644 docs/docs/integrations/document_loaders/parsers/azure_openai_whisper_parser.ipynb create mode 100644 libs/community/tests/examples/hello_world.m4a create mode 100644 libs/community/tests/unit_tests/document_loaders/parsers/test_azure_whisper_parser.py diff --git a/docs/docs/integrations/document_loaders/parsers/azure_openai_whisper_parser.ipynb b/docs/docs/integrations/document_loaders/parsers/azure_openai_whisper_parser.ipynb new file mode 100644 index 00000000000..b3dadb1f0ad --- /dev/null +++ b/docs/docs/integrations/document_loaders/parsers/azure_openai_whisper_parser.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Azure OpenAI Whisper Parser" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ">[Azure OpenAI Whisper Parser](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/whisper-overview) is a wrapper around the Azure OpenAI Whisper API which utilizes machine learning to transcribe audio files to english text. \n", + ">\n", + ">The Parser supports `.mp3`, `.mp4`, `.mpeg`, `.mpga`, `.m4a`, `.wav`, and `.webm`.\n", + "\n", + "The current implementation follows LangChain core principles and can be used with other loaders to handle both audio downloading and parsing. As a result of this the parser will `yield` an `Iterator[Document]`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The service requires Azure credentials, Azure endpoint and Whisper Model deployment, which can be set up by following the guide [here](https://learn.microsoft.com/en-us/azure/ai-services/openai/whisper-quickstart?tabs=command-line%2Cpython-new%2Cjavascript&pivots=programming-language-python). Furthermore, the required dependencies must be installed.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -Uq langchain langchain-community openai" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `AzureOpenAIWhisperParser`'s method, `.lazy_parse`, accepts a `Blob` object as a parameter containing the file path of the file to be transcribed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.documents.base import Blob\n", + "\n", + "audio_path = \"path/to/your/audio/file\"\n", + "audio_blob = Blob(path=audio_path)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_community.document_loaders.parsers.audio import AzureOpenAIWhisperParser\n", + "\n", + "endpoint = \"\"\n", + "key = \"\"\n", + "name = \"\"\n", + "\n", + "parser = AzureOpenAIWhisperParser(\n", + " api_key=key, azure_endpoint=endpoint, api_version=version, deployment_name=name\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "documents = parser.lazy_parse(blob=audio_blob)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for doc in documents:\n", + " print(doc.page_content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `AzureOpenAIWhisperParser` can also be used in conjuction with audio loaders, like the `YoutubeAudioLoader` with a `GenericLoader`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_community.document_loaders.blob_loaders.youtube_audio import (\n", + " YoutubeAudioLoader,\n", + ")\n", + "from langchain_community.document_loaders.generic import GenericLoader" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Must be a list\n", + "url = [\"www.youtube.url.com\"]\n", + "\n", + "save_dir = \"save/directory/\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "name = \"\"\n", + "\n", + "loader = GenericLoader(\n", + " YoutubeAudioLoader(url, save_dir), AzureOpenAIWhisperParser(deployment_name=name)\n", + ")\n", + "\n", + "docs = loader.load()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for doc in documents:\n", + " print(doc.page_content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/libs/community/langchain_community/document_loaders/parsers/audio.py b/libs/community/langchain_community/document_loaders/parsers/audio.py index abc08e561a5..32ced082601 100644 --- a/libs/community/langchain_community/document_loaders/parsers/audio.py +++ b/libs/community/langchain_community/document_loaders/parsers/audio.py @@ -1,7 +1,8 @@ +import io import logging import os import time -from typing import Any, Dict, Iterator, Literal, Optional, Tuple, Union +from typing import Any, Callable, Dict, Iterator, Literal, Optional, Tuple, Union from langchain_core.documents import Document @@ -12,6 +13,218 @@ from langchain_community.utils.openai import is_openai_v1 logger = logging.getLogger(__name__) +class AzureOpenAIWhisperParser(BaseBlobParser): + """ + Transcribe and parse audio files using Azure OpenAI Whisper. + + This parser integrates with the Azure OpenAI Whisper model to transcribe + audio files. It differs from the standard OpenAI Whisper parser, requiring + an Azure endpoint and credentials. The parser is limited to files under 25 MB. + + **Note**: + This parser uses the Azure OpenAI API, providing integration with the Azure + ecosystem, and making it suitable for workflows involving other Azure services. + + For files larger than 25 MB, consider using Azure AI Speech batch transcription: + https://learn.microsoft.com/azure/ai-services/speech-service/batch-transcription-create?pivots=rest-api#use-a-whisper-model + + Setup: + 1. Follow the instructions here to deploy Azure Whisper: + https://learn.microsoft.com/azure/ai-services/openai/whisper-quickstart?tabs=command-line%2Cpython-new&pivots=programming-language-python + 2. Install ``langchain`` and set the following environment variables: + + .. code-block:: bash + + pip install -U langchain langchain-community + + export AZURE_OPENAI_API_KEY="your-api-key" + export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/" + export OPENAI_API_VERSION="your-api-version" + + Example Usage: + .. code-block:: python + + from langchain.community import AzureOpenAIWhisperParser + + whisper_parser = AzureOpenAIWhisperParser( + deployment_name="your-whisper-deployment", + api_version="2024-06-01", + api_key="your-api-key", + # other params... + ) + + audio_blob = Blob(path="your-audio-file-path") + response = whisper_parser.lazy_parse(audio_blob) + + for document in response: + print(document.page_content) + + Integration with Other Loaders: + The AzureOpenAIWhisperParser can be used with video/audio loaders and + `GenericLoader` to automate retrieval and parsing. + + YoutubeAudioLoader Example: + .. code-block:: python + + from langchain_community.document_loaders.blob_loaders import ( + YoutubeAudioLoader + ) + from langchain_community.document_loaders.generic import GenericLoader + + # Must be a list + youtube_url = ["https://your-youtube-url"] + save_dir = "directory-to-download-videos" + + loader = GenericLoader( + YoutubeAudioLoader(youtube_url, save_dir), + AzureOpenAIWhisperParser(deployment_name="your-deployment-name") + ) + + docs = loader.load() + """ + + def __init__( + self, + *, + api_key: Optional[str] = None, + azure_endpoint: Optional[str] = None, + api_version: Optional[str] = None, + azure_ad_token_provider: Union[Callable[[], str], None] = None, + language: Optional[str] = None, + prompt: Optional[str] = None, + response_format: Union[ + Literal["json", "text", "srt", "verbose_json", "vtt"], None + ] = None, + temperature: Optional[float] = None, + deployment_name: str, + max_retries: int = 3, + ): + """ + Initialize the AzureOpenAIWhisperParser. + + Args: + api_key (Optional[str]): + Azure OpenAI API key. If not provided, defaults to the + `AZURE_OPENAI_API_KEY` environment variable. + azure_endpoint (Optional[str]): + Azure OpenAI service endpoint. Defaults to `AZURE_OPENAI_ENDPOINT` + environment variable if not set. + api_version (Optional[str]): + API version to use, + defaults to the `OPENAI_API_VERSION` environment variable. + azure_ad_token_provider (Union[Callable[[], str], None]): + Azure Active Directory token for authentication (if applicable). + language (Optional[str]): + Language in which the request should be processed. + prompt (Optional[str]): + Custom instructions or prompt for the Whisper model. + response_format (Union[str, None]): + The desired output format. Options: "json", "text", "srt", + "verbose_json", "vtt". + temperature (Optional[float]): + Controls the randomness of the model's output. + deployment_name (str): + The deployment name of the Whisper model. + max_retries (int): + Maximum number of retries for failed API requests. + Raises: + ImportError: + If the required package `openai` is not installed. + """ + self.api_key = api_key or os.environ.get("AZURE_OPENAI_API_KEY") + self.azure_endpoint = azure_endpoint or os.environ.get("AZURE_OPENAI_ENDPOINT") + self.api_version = api_version or os.environ.get("OPENAI_API_VERSION") + self.azure_ad_token_provider = azure_ad_token_provider + + self.language = language + self.prompt = prompt + self.response_format = response_format + self.temperature = temperature + + self.deployment_name = deployment_name + self.max_retries = max_retries + + try: + import openai + except ImportError: + raise ImportError( + "openai package not found, please install it with " + "`pip install openai`" + ) + + if is_openai_v1(): + self._client = openai.AzureOpenAI( + api_key=self.api_key, + azure_endpoint=self.azure_endpoint, + api_version=self.api_version, + max_retries=self.max_retries, + azure_ad_token=self.azure_ad_token_provider, + ) + else: + if self.api_key: + openai.api_key = self.api_key + if self.azure_endpoint: + openai.api_base = self.azure_endpoint + if self.api_version: + openai.api_version = self.api_version + openai.api_type = "azure" + self._client = openai + + @property + def _create_params(self) -> Dict[str, Any]: + params = { + "language": self.language, + "prompt": self.prompt, + "response_format": self.response_format, + "temperature": self.temperature, + } + return {k: v for k, v in params.items() if v is not None} + + def lazy_parse(self, blob: Blob) -> Iterator[Document]: + """ + Lazily parse the provided audio blob for transcription. + + Args: + blob (Blob): + The audio file in Blob format to be transcribed. + + Yields: + Document: + Parsed transcription from the audio file. + + Raises: + Exception: + If an error occurs during transcription. + """ + + file_obj = open(str(blob.path), "rb") + + # Transcribe + try: + if is_openai_v1(): + transcript = self._client.audio.transcriptions.create( + model=self.deployment_name, + file=file_obj, + **self._create_params, + ) + else: + transcript = self._client.Audio.transcribe( + model=self.deployment_name, + deployment_id=self.deployment_name, + file=file_obj, + **self._create_params, + ) + except Exception: + raise + + yield Document( + page_content=transcript.text + if not isinstance(transcript, str) + else transcript, + metadata={"source": blob.source}, + ) + + class OpenAIWhisperParser(BaseBlobParser): """Transcribe and parse audio files. @@ -19,7 +232,7 @@ class OpenAIWhisperParser(BaseBlobParser): Args: api_key: OpenAI API key - chunk_duration_threshold: minimum duration of a chunk in seconds + chunk_duration_threshold: Minimum duration of a chunk in seconds NOTE: According to the OpenAI API, the chunk duration should be at least 0.1 seconds. If the chunk duration is less or equal than the threshold, it will be skipped. @@ -61,8 +274,6 @@ class OpenAIWhisperParser(BaseBlobParser): def lazy_parse(self, blob: Blob) -> Iterator[Document]: """Lazily parse the blob.""" - import io - try: import openai except ImportError: @@ -85,11 +296,11 @@ class OpenAIWhisperParser(BaseBlobParser): if self.api_key: openai.api_key = self.api_key if self.base_url: - openai.base_url = self.base_url + openai.api_base = self.base_url # Audio file from disk - audio = AudioSegment.from_file(blob.path) + audio = AudioSegment.from_file(blob.path) # Define the duration of each chunk in minutes # Need to meet 25MB size limit for Whisper API chunk_duration = 20 @@ -240,8 +451,6 @@ class OpenAIWhisperParserLocal(BaseBlobParser): def lazy_parse(self, blob: Blob) -> Iterator[Document]: """Lazily parse the blob.""" - import io - try: from pydub import AudioSegment except ImportError: @@ -436,8 +645,6 @@ class FasterWhisperParser(BaseBlobParser): def lazy_parse(self, blob: Blob) -> Iterator[Document]: """Lazily parse the blob.""" - import io - try: from pydub import AudioSegment except ImportError: diff --git a/libs/community/tests/examples/hello_world.m4a b/libs/community/tests/examples/hello_world.m4a new file mode 100644 index 0000000000000000000000000000000000000000..959ddd19aa63a976671565444c70171b0030920f GIT binary patch literal 61634 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

_qeQVl-L$8aXxQcWE~-iP=!Px)=1=oOP$^j} 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

8cBi3#C&<{G{c4HzzyPb{){d6 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 an5vk;O@Jc6PT+S;BhgVn;Xpo0`2PSa@nLHK literal 0 HcmV?d00001 diff --git a/libs/community/tests/unit_tests/document_loaders/parsers/test_azure_whisper_parser.py b/libs/community/tests/unit_tests/document_loaders/parsers/test_azure_whisper_parser.py new file mode 100644 index 00000000000..d48970534c1 --- /dev/null +++ b/libs/community/tests/unit_tests/document_loaders/parsers/test_azure_whisper_parser.py @@ -0,0 +1,104 @@ +"""Tests for the Azure OpenAI Whisper parser.""" + +from pathlib import Path +from typing import Any +from unittest.mock import Mock, patch + +import pytest +from langchain_core.documents import Document +from langchain_core.documents.base import Blob + +from langchain_community.document_loaders.parsers.audio import AzureOpenAIWhisperParser + +_THIS_DIR = Path(__file__).parents[3] + +_EXAMPLES_DIR = _THIS_DIR / "examples" +AUDIO_M4A = _EXAMPLES_DIR / "hello_world.m4a" + + +@pytest.mark.requires("openai") +@patch("openai.AzureOpenAI") +def test_azure_openai_whisper(mock_client: Mock) -> None: + endpoint = "endpoint" + key = "key" + version = "115" + name = "model" + + parser = AzureOpenAIWhisperParser( + api_key=key, azure_endpoint=endpoint, api_version=version, deployment_name=name + ) + mock_client.assert_called_once_with( + api_key=key, + azure_endpoint=endpoint, + api_version=version, + max_retries=3, + azure_ad_token=None, + ) + assert parser._client == mock_client() + + +@pytest.mark.requires("openai") +def test_is_openai_v1_lazy_parse(mocker: Any) -> None: + endpoint = "endpoint" + key = "key" + version = "115" + name = "model" + + mock_blob = mocker.Mock(spec=Blob) + mock_blob.path = AUDIO_M4A + mock_blob.source = "test_source" + + mock_openai_client = mocker.Mock() + + mock_openai_client.audio.transcriptions.create.return_value = mocker.Mock() + mock_openai_client.audio.transcriptions.create.return_value.text = ( + "Transcribed text" + ) + + mocker.patch("langchain_community.utils.openai.is_openai_v1", return_value=True) + + parser = AzureOpenAIWhisperParser( + api_key=key, azure_endpoint=endpoint, api_version=version, deployment_name=name + ) + + parser._client = mock_openai_client + + result = list(parser.lazy_parse(mock_blob)) + + assert len(result) == 1 + assert isinstance(result[0], Document) + assert result[0].page_content == "Transcribed text" + assert result[0].metadata["source"] == "test_source" + + +@pytest.mark.requires("openai") +def test_is_not_openai_v1_lazy_parse(mocker: Any) -> None: + endpoint = "endpoint" + key = "key" + version = "115" + name = "model" + + mock_blob = mocker.Mock(spec=Blob) + mock_blob.path = AUDIO_M4A + mock_blob.source = "test_source" + + mock_openai_client = mocker.Mock() + + mock_openai_client.audio.transcriptions.create.return_value = mocker.Mock() + mock_openai_client.audio.transcriptions.create.return_value.text = ( + "Transcribed text" + ) + + mocker.patch("langchain_community.utils.openai.is_openai_v1", return_value=False) + + parser = AzureOpenAIWhisperParser( + api_key=key, azure_endpoint=endpoint, api_version=version, deployment_name=name + ) + parser._client = mock_openai_client + + result = list(parser.lazy_parse(mock_blob)) + + assert len(result) == 1 + assert isinstance(result[0], Document) + assert result[0].page_content == "Transcribed text" + assert result[0].metadata["source"] == "test_source"