From 1db0aacaceaa4d7a07faf2830e119290039c6a06 Mon Sep 17 00:00:00 2001 From: Paul Cornell Date: Mon, 31 Mar 2025 14:39:33 -0700 Subject: [PATCH 1/5] Self-hosted plugins --- self-hosted/plugins/overview.mdx | 17 +++++++ self-hosted/plugins/tutorial.mdx | 82 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 self-hosted/plugins/overview.mdx create mode 100644 self-hosted/plugins/tutorial.mdx diff --git a/self-hosted/plugins/overview.mdx b/self-hosted/plugins/overview.mdx new file mode 100644 index 00000000..1caa5585 --- /dev/null +++ b/self-hosted/plugins/overview.mdx @@ -0,0 +1,17 @@ +--- +title: Unstructured self-hosted plugins overview +sidebarTitle: Overview +--- + + + +Developing, deploying, and running Unstructured self-hosted plugins is available only for +the [Unstructured user interface](/ui/overview) (UI) that has already been deployed to +infrastructure that you maintain in your +[Amazon Web Services (AWS)](/self-hosted/aws/overview), [Azure](/self-hosted/azure/overview), or +[Google Cloud Platform (GCP)](/self-hosted/gcp/overview) account. + +If you do not already have a self-hosted deployment of the Unstructured UI, +contact your Unstructured sales representative, email Unstructured Sales at [sales@unstructured.io](mailto:sales@unstructured.io), or fill out the +[contact form](https://unstructured.io/contact) on the Unstructured website, and a member of the Unstructured sales or support teams +will get back to you as soon as possible to discuss self-hosting options. \ No newline at end of file diff --git a/self-hosted/plugins/tutorial.mdx b/self-hosted/plugins/tutorial.mdx new file mode 100644 index 00000000..03f50e08 --- /dev/null +++ b/self-hosted/plugins/tutorial.mdx @@ -0,0 +1,82 @@ +--- +title: Self-hosted plugins tutorial +sidebarTitle: Tutorial +--- + +This hands-on tutorial shows how to use the Unstructured self-hosted plugin framework to create a simple plugin. +This plugin will perform a basic operation. Specifically, this plugin will ... + +## Requirements + +- A [self-hosted](/self-hosted/overview) deployment of the Unstructured UI and Unstructured API into infrastructure that you maintain in your + Amazon Web Services (AWS), Azure, or Google Cloud Platform (GCP) account. +- A local development machine with [Docker Desktop](https://docs.docker.com/desktop/) and the Python pacakge and project manager + [uv](https://docs.astral.sh/uv/getting-started/installation/) installed. + +## Getting started + + + + + + + + + + +pip install utic-dev-tools cookiecutter + +uv init unstructured_plugin_demo +cd unstructured_plugin_demo +uv venv +source .venv/bin/activate +uv add utic-dev-tools cookiecutter + + +## Write the plugin + + + + + + + + + + +## Test the plugin locally + + + + + + + + + + +## Deploy the plugin to the UI + + + + + + + + + + +## Test the plugin in the UI + + + + + + + + + + +## Learn more + + From 3b63106d908ac5391ad158fe5b6b38df6c165d6f Mon Sep 17 00:00:00 2001 From: Paul Cornell Date: Mon, 31 Mar 2025 17:25:38 -0700 Subject: [PATCH 2/5] More initial tutorial content. --- self-hosted/plugins/tutorial.mdx | 215 +++++++++++++++++++++++++++++-- 1 file changed, 202 insertions(+), 13 deletions(-) diff --git a/self-hosted/plugins/tutorial.mdx b/self-hosted/plugins/tutorial.mdx index 03f50e08..45e031eb 100644 --- a/self-hosted/plugins/tutorial.mdx +++ b/self-hosted/plugins/tutorial.mdx @@ -3,43 +3,232 @@ title: Self-hosted plugins tutorial sidebarTitle: Tutorial --- -This hands-on tutorial shows how to use the Unstructured self-hosted plugin framework to create a simple plugin. -This plugin will perform a basic operation. Specifically, this plugin will ... +This hands-on tutorial shows how to use the Unstructured self-hosted plugin framework to create a sample plugin. +This sample plugin uses VertexAI to perform sentiment analysis on the text that Unstructured extracts from documents. For example, +given the following custom prompt: + +```text +Given a piece of text, classify the following types of information: +- toxic or non-toxic +- emotion if it conveys, such as happiness, or anger +- intent such as "finding information", "making a reservation", or "placing an order" + +text: + {text} + +Output the results as JSON and nothing else. For example: +```json +{{ + "toxicity": "non-toxic", + "emotion": "neutral", + "intent": "making a reservation" +}} +``` + +And the following text: + +```text +Hi, can you please book a table for two at Juan for May 1? +``` + +VertexAI returns a sentiment analysis in this format: + +```json +{ + "toxicity": "non-toxic", + "emotion": "neutral", + "intent": "making a reservation" +} +``` ## Requirements - A [self-hosted](/self-hosted/overview) deployment of the Unstructured UI and Unstructured API into infrastructure that you maintain in your Amazon Web Services (AWS), Azure, or Google Cloud Platform (GCP) account. +- A Docker - A local development machine with [Docker Desktop](https://docs.docker.com/desktop/) and the Python pacakge and project manager [uv](https://docs.astral.sh/uv/getting-started/installation/) installed. ## Getting started - + + Create use a directory on your local machine that you want to use plugin development. If you create a new directory, be + sure to switch to it after you create it. This tutorial uses a directory named `plugins` within the + current working directory. For example: + + ```bash + mkdir plugins + cd plugins + ``` - + + Use `uv` to create a virtual environment within the directory that you want to use for plugin development. After you + create the virtual environment, activate it. + + This tutorial uses a virtual environment named + `plugins_3_12_9`. This virtual environment uses Python 3.12.9. If this Python version is not installed + on the system, `uv` installs it first. For example: + + ```bash + uv venv --python 3.12.9 --prompt "plugins_3_12_9" + source .venv/bin/activate + ``` - + + Use `uv` to install the Unstructured plugin development tools and their dependencies: -pip install utic-dev-tools cookiecutter + ```bash + uv pip install utic-dev-tools cookiecutter + ``` -uv init unstructured_plugin_demo -cd unstructured_plugin_demo -uv venv -source .venv/bin/activate -uv add utic-dev-tools cookiecutter + The `cookiecutter` package is a command-line utility that uses techniques such as wizards along with Python project templates to + initialize new projects based on user input. + + + 1. Use the `unstructured-plugins new` command to create a new plugin development project. This command starts a wizard that is used + to create a new directory for the plugin and then creates the plugin's starter files and directories within that directory: + + ```bash + unstructured-plugins new + ``` + + 2. When propmpted, enter some display name for the plugin, and then press `Enter`. This tutorial uses `Sentiment Analysis` as the plugin's display name: + + ```bash + [1/3] name (My First Plugin): Sentiment Analysis + ``` + + 3. Next, enter the plugin's type, and then press `Enter`. This tutorial uses `sentiment` as the plugin's type: + + ```bash + [2/3] type (): sentiment + ``` + + 4. Next, enter the plugin's subtype, and then press `Enter`. This tutorial uses `analysis` as the plugin's subtype: + + ```bash + [3/3] subtype (): analysis + ``` + + 5. A project folder is created within the `plugins` directory. The project folder is named `plugin-` followed by the + plugin's type and subtype. For this tutorial, the project folder's name is named `plugin-sentiment-analysis`. + + Switch to the plugin's project folder and then use `uv` to install and update the project's code dependencies: + + ```bash + cd plugin-sentiment-analysis + uv sync + ``` + + + ## Write the plugin - + + In this step, you add the user interface settings for the plugin. The user interface settings are the fields that + users see when they add the plugin as a node to a workflow's DAG. The user interface settings are defined in the `__init__.py` file + of the plugin project's `src/plugin__` subfolder. These settings are specified in the + `__init__.py` file's `PluginSettings` class, which is a subclass of + the Pydantic `BaseModel` class. The `BaseModel` class provides a Pydantic implementation of various type validation, + data parsing, and serialization functionality. + + 1. In the project's `src` directory, under the `plugin_sentiment_analyis` subdirectory, open the `__init__.py` file. + 2. In the `__init__.py` file, add the necessary VertexAI settings. First, add the following import statements to the top of the file: + + ```python + from typing import Literal + from pydantic import SecretStr + ``` + + The `Literal` is a type hint in Python that restricts a field to specific literal values (such as strings, numbers, or booleans). + It enforces that the input must match one of the specified options. + + The `SecretStr` is a specialized string type in Pydantic for sensitive data (such as passwords and API keys). It masks the value in + fields by displaying `*****`. + + 3. In the `__init__.py` file's `PluginSettings` class, replace the sample `string_field` setting definition with settings for the `location`, `credentials`, and + `model` fields. The class definition should now look as follows: + + ```python + class PluginSettings(BaseModel): + """ + Settings used to configure running instances of the plugin. + + These are what can be configured by the user and what will be + available in the UI. + """ + + location: Literal[ + "us-east5", "us-south1", "us-central1", "us-east1", "us-east4", "us-west1" + ] = Field(title="API Location") + credentials: SecretStr = Field(title="Credentials JSON") + model: Literal["gemini-1.5-flash"] = Field("gemini-1.5-flash", title="Model") + ``` + + - The `location` field specifies the location of the VertexAI API. The field in the UI's help pane for the plugin node + will display the title of `API Location`. + - The `credentials` field specifies the JSON credentials for the VertexAI API. The field in the UI will have the title of + `Credentials JSON`. Specifying the `SecretStr` type displays the field's text with asterisks. + - The `model` field specifies the model for VertexAI to use. The field in the UI will have the title of `Model`. The default + value for this field is `gemini-1.5-flash`. - + + 1. Add the necessary VertexAI dependencies: + + ```bash + uv pip install google-cloud-aiplatform google-auth + ``` + + 2. At the top of the `__init__.py` file, add the necessary import statements for VertexAI and for logging and JSON parsing: + + ```python + from google.cloud import aiplatform + from google.oauth2 import service_account + from vertexai.generative_models import GenerativeModel + import logging, json + ``` + + 3. In the `__init__.py` file's `Plugin` class, replace the `__post_init__` function body with the following definition. + The function body definition should now look as follows: + + ```python + def __post_init__(self): + with open(self.env_settings.job_settings_file) as f: + self.plugin_settings = PluginSettings.model_validate_json(f.read()) + + credentials_json = json.loads( + self.plugin_settings.credentials.get_secret_value() + ) + credentials = service_account.Credentials.from_service_account_info( + credentials_json + ) + + aiplatform.init( + location=self.plugin_settings.location, + project=credentials_json["project_id"], + credentials=credentials, + ) + + self.model = GenerativeModel(self.plugin_settings.model) + ``` + + 4. In the `__init__.py` file's `Plugin` class, just before the `run` function, add the ... + + ```python + ``` + + 5. In the `__init__.py` file's `Plugin` class, replace the `run` function body with the following definition. + The function body definition should now look as follows: + + ```python + ``` From 9da42c065a2b5455ed692d87e75212b559d4ccee Mon Sep 17 00:00:00 2001 From: Paul Cornell Date: Tue, 1 Apr 2025 13:55:52 -0700 Subject: [PATCH 3/5] More tutorial steps --- self-hosted/plugins/tutorial.mdx | 443 +++++++++++++++++++++++++++++-- 1 file changed, 416 insertions(+), 27 deletions(-) diff --git a/self-hosted/plugins/tutorial.mdx b/self-hosted/plugins/tutorial.mdx index 45e031eb..1bc31b07 100644 --- a/self-hosted/plugins/tutorial.mdx +++ b/self-hosted/plugins/tutorial.mdx @@ -45,15 +45,49 @@ VertexAI returns a sentiment analysis in this format: - A [self-hosted](/self-hosted/overview) deployment of the Unstructured UI and Unstructured API into infrastructure that you maintain in your Amazon Web Services (AWS), Azure, or Google Cloud Platform (GCP) account. -- A Docker - A local development machine with [Docker Desktop](https://docs.docker.com/desktop/) and the Python pacakge and project manager [uv](https://docs.astral.sh/uv/getting-started/installation/) installed. +- For sending requests to the plugin through Docker locally, the [curl](https://curl.se/) utility installed on the development machine. +- For deploying the plugin to your self-hosted Unstructured UI, you must have aceess to a container registry that is compliant with the Open Container Initiative (OCI) and + that is also reachable from your AWS, Azure, or GCP account. For example: + + - For AWS accounts, [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/) (Amazon ECR). + - For Azure accounts, [Azure Container Registry](https://azure.microsoft.com/products/container-registry). + - For GCP accounts, [Google Artifact Registry](https://cloud.google.com/artifact-registry) (GAR). + +- To call the VertexAI portions of this tutorial: + + - A [Google Cloud account](https://console.cloud.google.com). + - The **Vertex AI API** enabled in the account. [Learn how](https://cloud.google.com/apis/docs/getting-started#enabling_apis). + - Within the account, a Google Cloud service account and its related `credentials.json` key file or its contents in JSON format. + [Create a service account](https://developers.google.com/workspace/guides/create-credentials#create_a_service_account). + [Create credentials for a service account](https://developers.google.com/workspace/guides/create-credentials#create_credentials_for_a_service_account). + - A single-line string that contains the contents of the downloaded `credentials.json` key file for the service account (and not the service account key file itself). + To print this single-line string without line breaks, suitable for copying, you can run one of the following commands from your Terminal or Command Prompt. + In this command, replace `` with the path to the `credentials.json` key file that you downloaded by following the preceding instructions. + + - For macOS or Linux: + + ```text + tr -d '\n' < + ``` + + - For Windows: + + ```text + (Get-Content -Path "" -Raw).Replace("`r`n", "").Replace("`n", "") + ``` ## Getting started +In this section, you set up the local development environment for this tutorial's plugin. +This includes creating a directory for overall plugin development, +creating a virtual environment to isolate and version Python and various code dependencies, installing the Unstructured plugin development tools and their dependencies, +and creating and initializing the code project for this tutorial's plugin. + - - Create use a directory on your local machine that you want to use plugin development. If you create a new directory, be + + We recommend creating or using a centralized directory on your local machine to use for developing this and other plugins. If you create a new directory, be sure to switch to it after you create it. This tutorial uses a directory named `plugins` within the current working directory. For example: @@ -63,8 +97,8 @@ VertexAI returns a sentiment analysis in this format: ``` - - Use `uv` to create a virtual environment within the directory that you want to use for plugin development. After you + + Use `uv` to create a virtual environment within the directory that you want to use for overall plugin development. After you create the virtual environment, activate it. This tutorial uses a virtual environment named @@ -77,8 +111,8 @@ VertexAI returns a sentiment analysis in this format: ``` - - Use `uv` to install the Unstructured plugin development tools and their dependencies: + + Use `uv` to install the Unstructured plugin development tools and their dependencies into this virtual environment: ```bash uv pip install utic-dev-tools cookiecutter @@ -88,9 +122,9 @@ VertexAI returns a sentiment analysis in this format: initialize new projects based on user input. - - 1. Use the `unstructured-plugins new` command to create a new plugin development project. This command starts a wizard that is used - to create a new directory for the plugin and then creates the plugin's starter files and directories within that directory: + + 1. Use the `unstructured-plugins new` command to create the starter code for this tutorial's plugin development project. This command starts a wizard that is used + to create a new directory for developing this plugin and then creates the plugin's starter files and subdirectories within that directory: ```bash unstructured-plugins new @@ -114,10 +148,10 @@ VertexAI returns a sentiment analysis in this format: [3/3] subtype (): analysis ``` - 5. A project folder is created within the `plugins` directory. The project folder is named `plugin-` followed by the - plugin's type and subtype. For this tutorial, the project folder's name is named `plugin-sentiment-analysis`. + 5. A project folder is created within the centralized `plugins` directory. The project folder is named `plugin-` followed by the + plugin's type, another dash, and the plugin's subtype. For this tutorial, the project folder's name is named `plugin-sentiment-analysis`. - Switch to the plugin's project folder and then use `uv` to install and update the project's code dependencies: + Switch to the plugin's project folder and then use `uv` to install and update the project's specific code dependencies: ```bash cd plugin-sentiment-analysis @@ -129,17 +163,19 @@ VertexAI returns a sentiment analysis in this format: ## Write the plugin +In this section, you write the plugin's runtime logic. This tutorial's logic is primarily within the project's `src/plugin_sentiment_analysis/__init__.py` file. + - In this step, you add the user interface settings for the plugin. The user interface settings are the fields that - users see when they add the plugin as a node to a workflow's DAG. The user interface settings are defined in the `__init__.py` file + In this step, you add the user interface (UI) settings for the plugin. The UI settings are the fields that + users see when they add the plugin as a node to a workflow's visual DAG designer in the UI. The UI settings are defined in the `__init__.py` file of the plugin project's `src/plugin__` subfolder. These settings are specified in the `__init__.py` file's `PluginSettings` class, which is a subclass of - the Pydantic `BaseModel` class. The `BaseModel` class provides a Pydantic implementation of various type validation, + the [Pydantic](https://docs.pydantic.dev/latest/) `BaseModel` class. The `BaseModel` class provides a Pydantic implementation of various type validation, data parsing, and serialization functionality. 1. In the project's `src` directory, under the `plugin_sentiment_analyis` subdirectory, open the `__init__.py` file. - 2. In the `__init__.py` file, add the necessary VertexAI settings. First, add the following import statements to the top of the file: + 2. In the `__init__.py` file, add the necessary imports to capture VertexAI settings that the user sets in the UI. To do this, add the following `from...import` statements to the top of the file: ```python from typing import Literal @@ -177,6 +213,8 @@ VertexAI returns a sentiment analysis in this format: `Credentials JSON`. Specifying the `SecretStr` type displays the field's text with asterisks. - The `model` field specifies the model for VertexAI to use. The field in the UI will have the title of `Model`. The default value for this field is `gemini-1.5-flash`. + - At run time, the `PluginSettings` class reads these field's values from the UI and writes them as a JSON dictionary into a `settings.json` file + in the project's root. @@ -186,7 +224,7 @@ VertexAI returns a sentiment analysis in this format: uv pip install google-cloud-aiplatform google-auth ``` - 2. At the top of the `__init__.py` file, add the necessary import statements for VertexAI and for logging and JSON parsing: + 2. At the top of the `__init__.py` file, add the necessary import statements for calling the VertexAI API and for standard Python logging and JSON parsing: ```python from google.cloud import aiplatform @@ -195,8 +233,7 @@ VertexAI returns a sentiment analysis in this format: import logging, json ``` - 3. In the `__init__.py` file's `Plugin` class, replace the `__post_init__` function body with the following definition. - The function body definition should now look as follows: + 3. In the `__init__.py` file's `Plugin` class, replace the `__post_init__` function body with the following definition: ```python def __post_init__(self): @@ -219,38 +256,390 @@ VertexAI returns a sentiment analysis in this format: self.model = GenerativeModel(self.plugin_settings.model) ``` - 4. In the `__init__.py` file's `Plugin` class, just before the `run` function, add the ... + - The `__post_init__` function is called after the `Plugin` class is initialized. The function reads in the UI field values from the `settings.json` file + that the `PluginSettings` class wrote to earlier. + - The function then prepares the authorization credentials that were provided in the UI to be used by VertexAI. + - The `aiplatform.init` function initializes the VertexAI API with the specified location, project ID, and authorization credentials. + - The `GenerativeModel` class gets the model to be used that was specified in the UI. + + 4. In the `__init__.py` file's `Plugin` class, just before the `run` function, add the prompt text to be sent to VertexAI. + At run time, this prompt, along with a piece of text that Unstructured extracts from the document, is sent to VertexAI for sentiment analysis: ```python + PROMPT = """ + Given a piece of text, classify the following types of information: + - toxic or non-toxic + - emotion if it conveys, such as happiness, or anger + - intent such as "finding information", "making a reservation", or "placing an order" + + text: + {text} + + Output the results as JSON and nothing else. For example: + ```json + {{ + "toxicity": "non-toxic", + "emotion": "neutral", + "intent": "making a reservation" + }} + ``` + """ ``` - 5. In the `__init__.py` file's `Plugin` class, replace the `run` function body with the following definition. - The function body definition should now look as follows: + 5. In the `__init__.py` file's `Plugin` class, replace the `run` function body with the following definition: ```python + def run(self, element_dicts: list[dict]) -> Response: + """ + This method is called once for every file that is processed. + + element_dicts is a list of elements: + + See https://docs.unstructured.io/open-source/concepts/document-elements + """ + for element in element_dicts: + element: ElementDict + prompt_text = self.PROMPT.format(text=element["text"]) + response_text = self.model.generate_content(prompt_text).text + try: + data = json.loads(response_text.strip().strip("```").lstrip("json")) + except json.JSONDecodeError: + logging.basicConfig(level=logging.INFO) + logging.getLogger().error(f"Failed to parse response: {response_text}") + data = {} + element["metadata"].update(data) + + return Response(element_dicts=element_dicts) ``` + - The `run` function is called once for every file that is processed. The function takes a list of the elements that Unstructured generated from the file as input. + - Each element in the list of elements is a dictionary that contains the text extracted from the document and its related metadata. + - The function sends the prompt and the element's text to the model. + - The function then adds the sentiment analysis output to the element's `metadata` field. + - After the last element's sentiment analysis is output into the last element's `metadata` field, the enitre updated list's contents are given as input into the + next node in the workflow's DAG. + -## Test the plugin locally +## Run plugin tests locally with pytest + +In this section, you run the plugin's tests locally using `pytest` to make sure that the plugin's logic is working as expected before further +testing in Docker and eventual deployment for use in the UI. + 1. Add the necessary `pytest` dependencies. Also add a dependency on the `dotenv` package, which is used to read environment variables from a local `.env` file: + + ```bash + uv pip install pytest dotenv + ``` + + 2. In the project's `test` directory, at the top of the `test_plugin.py` file, add the following import statements to enable reading local environment variables. + Also, call the `load_dotenv` function to load the environment variables from the `.env` file: + + ```python + import os + from dotenv import load_dotenv + + load_dotenv() + ``` + + 3. In the `test_plugin.py` file, update the following `from...import` statement to find the specified classes that are defined in the `src/plugin_sentiment_analyis` folder: + + ```python + from src.plugin_sentiment_analysis import Plugin, PluginSettings + ``` + 1. In the `test_plugin.py` file, replace the `plugin` function body with the following definition: + + ```python + @pytest.fixture + def plugin(tmp_path): + credentials = os.getenv("VERTEXAI_CREDENTIALS") + if not credentials: + raise ValueError("VERTEXAI_CREDENTIALS env var must be set to run test") + + settings_filepath: Path = tmp_path / "settings.json" + settings = {"location": "us-west1", "credentials": credentials} + settings_filepath.write_text(json.dumps(settings)) + + yield Plugin( + env_settings=EnvSettings( + shared_filepath=tmp_path, + job_settings_file=str(settings_filepath), + ) + ) + ``` + + - The `plugin` function is a fixture that sets up the plugin's infrastructure for the `test_plugin` test function that follows. + - + + 2. In the project's root, create a file named `.env`. In this file, add an environment variable named `VERTEXAI_CREDENTIALS`, and set it to the single-line representation of the `credentials.json` file that + you generated in this tutorial's requirements: + + ```text + VERTEXAI_CREDENTIALS="" + ``` + + + If you plan to publish this plugin's source code to an external repository such as GitHub, do not include the `.env` file in the repository, as it + can expose sentitive information publicly, such as your credentials for the VertexAI API. + + To help prevent this file from accidentally being included in the repository, add a `.env` entry to a `.gitignore` file in the root of the project. + + + 3. In the `test_plugin.py` file, replace the `test_plugin` function body with the following definition. + The function body definition should now look as follows: + + ```python + def test_plugin(plugin: Plugin, elements: list[dict]): + + elements[0]["text"] = "Hi, can you please book a table for two at Juan for May 1?" + + output = plugin.run(elements) + output_elements = output.element_dicts + + assert len(output_elements) == 1 + metadata = output_elements[0]["metadata"] + + assert metadata["toxicity"] == "non-toxic" + assert metadata["emotion"] == "neutral" + assert metadata["intent"] == "making a reservation" + ``` + + - The `test_plugin` function is a test case that uses the `plugin` fixture to run the plugin's logic. + - The function takes a list of elements as input. The first element in the list contains the text that is used to test the plugin. + - The function then runs the plugin's logic and checks that the output is as expected. + - The function checks that the output contains the expected values for the `toxicity`, `emotion`, and `intent` fields that are returned. If the expected values + match, the test passes. Otherwise, the test fails. + + + To run the test, use the following command to run `pytest` though the `test` target in a file named `Makefile` in the root of the project: + + ```bash + make test + ``` + + If the test passes, you should see something similar to the following: + + ```bash + tests/test_plugin.py . + + 1 passed + ``` -## Deploy the plugin to the UI +## Run the plugin in Docker locally + +In this section, you proceed with local testing by running the plugin in Docker locally. +This allows you to test the plugin's logic in an isolated environment before you deploy it into your self-hosted UI. - + + In your local machine's home directory, create a hidden file named `.vertex-plugin-settings.json`. This file contains + information that your local installation of Docker passes into the running container. In this file, add the following JSON content: + + ```json + { + "location": "", + "credentials": "" + } + ``` + + In the preceding JSON: + + - Replace `` with the location of the VertexAI API that you want to use, for example, `us-west1`. + - Replace `` with the single-line representation of the `credentials.json` file that + you generated in this tutorial's requirements. + + + + 1. In the file named `Makefile` in the root of the project, replace the `.PHONY: run-docker` definition with the following definition: + + ```text + .PHONY: run-docker + run-docker: docker-build-local + docker run -it --rm \ + -v $(PWD):/shared \ + -v $(HOME)/.vertex-plugin-settings.json:/settings.json \ + -e JOB_SETTINGS_FILE=/settings.json \ + -p 8000:8000 \ + "${IMAGE_REPOSITORY}:${VERSION}" + ``` + + - The `run-docker` target builds the Docker image locally and then runs it as a container representing the plugin. + + 2. Start Docker Desktop on your local machine, if it is not already running. + 3. Run the following command to call the `run-docker` target, which builds the Docker image and then runs the resulting container, representing the plugin: + + ```bash + make run-docker + ``` + + You must leave this terminal window open and running while you are testing the plugin locally within the running Docker container. If you interrupt the running process here or close this + terminal window, the Docker container stops running and the plugin stops working. + + + + 1. In a new terminal window, use the following `curl` command to send a request to the plugin that is running in the Docker container. + The request contains some sample text that you want VertexAI to perform sentiment analysis on along with some pretend metadata in the format that is + typically generated by Unstructured during processing. + + ```bash + curl --location 'localhost:8000/invoke' \ + --header 'Content-Type: application/json' \ + --data '{ + "element_dicts": [ + { + "type": "NarrativeText", + "element_id": "1453c80530ef11712374570a086dbd64", + "text": "Hi, can you please book a table for two at Juan for May 1?", + "metadata": { + "languages": [ + "eng" + ], + "filetype": "text/plain", + "data_source": { + "record_locator": { + "path": "/path/to/file.txt" + }, + "permissions_data": [ + { + "mode": 33188 + } + ] + } + } + } + ] + }' + ``` + + 2. If successful, the output should look similar to the following. Notice that the `toxicity`, `emotion`, and `intent` fields were added to the + element's `metadata` field (JSON formatting has been applied here for better readability): + + ```json + { + "usage": [], + "status_code": 200, + "filedata_meta": { + "terminate_current": false, + "new_records": [] + }, + "status_code_text": null, + "output": { + "element_dicts": [ + { + "type": "NarrativeText", + "element_id": "1453c80530ef11712374570a086dbd64", + "text": "Hi, can you please book a table for two at Juan for May 1?", + "metadata": { + "languages": [ + "eng" + ], + "filetype": "text/plain", + "data_source": { + "record_locator": { + "path": "/path/to/file.txt" + }, + "permissions_data": [ + { + "mode": 33188 + } + ] + }, + "toxicity": "non-toxic", + "emotion": "neutral", + "intent": "making a reservation" + } + } + ] + }, + "message_channels": { + "infos": [], + "warnings": [] + } + } + ``` + + 3. When you are done testing, you can stop the plugin by interrupting or closing the terminal window where the Docker container is running. + + + +## Deploy the plugin to your self-hosted UI + +In this section, you deploy the successfully-tested plugin for your users to add to their workflows' DAGs within your self-hosted Unstructured UI. +This section describes how to deploy the plugin from your local development machine directly into your existing container registry. In practice, you +would typically use a continuous integration and continuous deployment (CI/CD) pipeline to automate this process through more robust means. + + + + In the file named `Makefile` in the root of the project, set the `IMAGE_REGISTRY` variable, replacing `REGISTRY_NAME_REPLACE_ME` with the name of your container registry. + + ```text + IMAGE_REGISTRY=REGISTRY_NAME_REPLACE_ME + ``` + + To get the name of your container registry if you do not already know it, run the command that is appropriate for your container registry. For example: + + - For AWS ECR, run the AWS CLI command [aws ecr describe-repositories](https://docs.aws.amazon.com/cli/latest/reference/ecr/describe-repositories.html) with the appropriate command-line options. + - For Azure Container Registry, run the Azure CLI command [az acr list](https://learn.microsoft.com/en-us/cli/azure/acr?view=azure-cli-latest#az-acr-list) with the appropriate command-line options. + - For GAR, run the Google Cloud CLI command [gcloud artifacts repositories list](https://cloud.google.com/sdk/gcloud/reference/artifacts/repositories/list) with the appropriate command-line options. + + The container registry name typically takes the following format: + + - For AWS ECR, `.dkr.ecr..amazonaws.com` + - For Azure Container Registry, `.azurecr.io` + - For GAR, `-docker.pkg.dev//` - + + Set the following environment variables to the appropriate username and password for access to your container registry: + + - `PLUGIN_REGISTRY_USERNAME` + - `PLUGIN_REGISTRY_PASSWORD` + + For example: + + ```bash + # For macOS and Linux: + export PLUGIN_REGISTRY_USERNAME="" + + export PLUGIN_REGISTRY_PASSWORD="" + + # For Windows: + set PLUGIN_REGISTRY_USERNAME="" + + set PLUGIN_REGISTRY_PASSWORD="" + ``` + + In the preceding commands, for ``, run the command that is appropriate for your container registry. For example: + + - For AWS ECR, you do not run a separate login command here. + - For Azure Container Registry, run the Azure CLI command [az acr login](https://learn.microsoft.com/en-us/cli/azure/acr?view=azure-cli-latest#az-acr-login) with the appropriate command-line options. + - For GAR, run the Google Cloud CLI command [gcloud auth configure-docker](https://cloud.google.com/sdk/gcloud/reference/auth/configure-docker) with the appropriate command-line options. + + In the preceding commands, to get the value for ``, run the command that is appropriate for your container registry. For example: + + - For AWS ECR, run the AWS CLI command [aws ecr get-login-password](https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login-password.html) with the appropriate command-line options. + - For Azure Container Registry, run the Azure CLI command [az acr credential show](https://learn.microsoft.com/en-us/cli/azure/acr/credential?view=azure-cli-latest#az-acr-credential-show) with the appropriate command-line options. + - For GAR, run the Google Cloud CLI command [gcloud auth print-access-token](https://cloud.google.com/sdk/gcloud/reference/auth/print-access-token) with the appropriate command-line options. + + + + Run the following commands, one command at a time, to build the plugin's container and then deploy it to your container registry: + + ```bash + make docker-build + make docker-push + make publish-plugin + make promote-plugin-to-staging + ``` From 5e0812dd2eca0f409aa0467dbdf04de6662aec3b Mon Sep 17 00:00:00 2001 From: Paul Cornell Date: Tue, 1 Apr 2025 16:39:17 -0700 Subject: [PATCH 4/5] Added overview and conceptual diagram --- docs.json | 7 ++ img/ui/Plugins-DAG.png | Bin 0 -> 114946 bytes self-hosted/plugins/overview.mdx | 28 ++++++- self-hosted/plugins/tutorial.mdx | 130 +++++++++++++++++++++++-------- 4 files changed, 128 insertions(+), 37 deletions(-) create mode 100644 img/ui/Plugins-DAG.png diff --git a/docs.json b/docs.json index 56b3402a..cc02562f 100644 --- a/docs.json +++ b/docs.json @@ -482,6 +482,13 @@ "self-hosted/gcp/overview", "self-hosted/gcp/onboard" ] + }, + { + "group": "Plugins", + "pages": [ + "self-hosted/plugins/overview", + "self-hosted/plugins/tutorial" + ] } ] }, diff --git a/img/ui/Plugins-DAG.png b/img/ui/Plugins-DAG.png new file mode 100644 index 0000000000000000000000000000000000000000..68b18697b128113886752893444be78ac7619d90 GIT binary patch literal 114946 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 z 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≈n2+7A2h;j<=N#*0VS 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¨@p`5GlS4Pif8rE>(qn zoeIip&DlxT80_cV8aaeZbzt{CK(82Io|sFolvc5M*r4sv@;?7(1~nJBUR|R(_6T>; z+?ieiTb>;eN5`X*45d--p7id~K&Ppm92tom*gRIKMXAw4dam`c^y)wG z1gz8!x&@K*orfC_Kah7KNg_1|PH@jo_Meze@A-kswlDx?E_F64(oA)p{(QhewS%CN zx__u}A&?QAiMdUeM~}&qcv}Nryx65>4Btc%%+j+jYLc#)RsJlpL>zGOE5r=Nn=71= z#9?zN9LhPl-Dp_k94=f1yL69sE3CM?a*(f+3Ee`ndqM|WJ}!4=(lB)DYPbHUbcsCK zdiI`BRi^=BM2>1>iaCUWCobYB{Tb;+C|JZQMU26;8RX75J}gp+Kh4Udu)6nZU@IxG za^<|N?oGAJMTCmthj+!1P&-%lS|-3!!^$}UJ>4?-U8XHd5UNE3M^T|25tX1mMx)#p zWlC5|!+jII1yCYB(T`vG*_u+_74h#MGL-yJ@B&JPyoH9wti&rNaJH%l1e3IM&xnH4 z1Pz)4GD$9B#cCEshh7e%3Itfx#`tqPT+PTb1d@teRUIGcFt)yR?ozmNu?e$ca&NyrYAO<}>8l}dkNpf4w zpTftZU6o1N%{QlLe+_-lSb5mUEw)-Br46XHw;cWQ4w$CI)j2xM!JBVPc8NV zy%&WNF`js{m+DqVA3H=25LO|?_`b`vb~VLe+(1YP$e_h~8bINSy5+IP$Fq_DD8KCajW#WssWd1fOI9HT`o z00M%|Qd0911x2AVz(Oww<-OGGTgzS}Jf6O4;hBTM%I%>(hDtgP$Daq5I~un*>Hy?r zBEv2x#8mvKg0!za+=R!4C($LVUM9DhHA7+iFe5!?$;WJ;c@95Tju))7>Xh?C2&D*x z2yZ0wCzs9kTy%oMKv9MxAc!+kkdO>il9W>0tYe%^vx7i6=W@-M6@ zo}%(ui!N)q6jOe8`Az*R$;(TzwBr*61rubjO@YJj_kVE(!UMGfHf>7{0?Emh&E+vl zI||(26Ma9bRGutmLcfMzA!)~*71zP0smW^{E#EnHN`&JE6sz8u2EuPne|<7EazfY0 z>kfONx!q7AVrS-MBvix)kGP~d00D83$U9OIemgpLQCdB&{FbI{WNq2W-$>SfsR1O5v5irg%u+W53 zqXl(ymR|HfQp_c7|6vN_@orYj#J&!jmx`<4X?PYJ8{24+$iUo43ho!VRL|<>1vE|} zr|Gft`z|lE(n)75t+(bXRtYx)+}!g1z#P~drGf6Db`S$~^xk^RK}B04JC)ZrA`$9L<~5FYr$g-AZo82tP7z$O+@}LC_JThjE zIrBR%MUJY3XgI+^THz~rt~sa_JMVCFnq6FLqBeNweoT#0Pp!OWWYKeC#z^>%kktW@wPzM&6Q4x9ew1LVc)&iy$4mQw?6&oE9}BAe&{nL`Y)zL z77)>}o}?{Z5Gwi5Cc6jdxh_=+vsh+$rZY!!!b);1OY%EWa`b6%#3`Zn1T_3rKZTf3 z&h#gQ3R%%}q9UYzHlyu2rblc-d zVryo{t~&t@DGJsD616TDds|Ab*tsThdH8jo1df8M z2JSiNOrZ{lM52&^A!XA%*i7#QKJ2+WxDAUEr_BEAE%aEc0%t=?jR=ysW6)H2HEYw_ z2a~n=kT}rDZ*lcfpFA2&!5H|+*5+<7c|FO`e;kq795=<>ik-Q3=;mwPeG6<;{Aj`o8rNpQ7_`_JF{fThZ0${iaV~qy`}=AdVdkBykpa{MjeY+LUdn z1SI1Ae0T#Bguj=4d8X~>6$~0Gfzk#@bz%SWlfS?1fJL?hjSx)5wu7mE{rm5xKl`%{ z285%9y6*qi+rNfO2m?NCpSL2{e~gWSQ3vjBy~sN8=VYisKod9oZ$ - We recommend creating or using a centralized directory on your local machine to use for developing this and other plugins. If you create a new directory, be + We recommend creating or using a centralized directory on your local development machine to use for developing this and other plugins. If you create a new directory, be sure to switch to it after you create it. This tutorial uses a directory named `plugins` within the current working directory. For example: @@ -112,13 +121,14 @@ and creating and initializing the code project for this tutorial's plugin. - Use `uv` to install the Unstructured plugin development tools and their dependencies into this virtual environment: + Use `uv` to install the Unstructured plugin development tools and their dependencies into this virtual environment. These tools and their + dependencies will be the same for all plugins that you develop that use this virtual environment. ```bash uv pip install utic-dev-tools cookiecutter ``` - The `cookiecutter` package is a command-line utility that uses techniques such as wizards along with Python project templates to + The dependent `cookiecutter` package is a command-line utility that uses techniques such as wizards along with Python project templates to initialize new projects based on user input. @@ -151,7 +161,7 @@ and creating and initializing the code project for this tutorial's plugin. 5. A project folder is created within the centralized `plugins` directory. The project folder is named `plugin-` followed by the plugin's type, another dash, and the plugin's subtype. For this tutorial, the project folder's name is named `plugin-sentiment-analysis`. - Switch to the plugin's project folder and then use `uv` to install and update the project's specific code dependencies: + Switch to the plugin's project folder and then use `uv` to install and update this project's specific code dependencies: ```bash cd plugin-sentiment-analysis @@ -208,13 +218,13 @@ In this section, you write the plugin's runtime logic. This tutorial's logic is ``` - The `location` field specifies the location of the VertexAI API. The field in the UI's help pane for the plugin node - will display the title of `API Location`. + will display the title of **API Location**. - The `credentials` field specifies the JSON credentials for the VertexAI API. The field in the UI will have the title of - `Credentials JSON`. Specifying the `SecretStr` type displays the field's text with asterisks. - - The `model` field specifies the model for VertexAI to use. The field in the UI will have the title of `Model`. The default + **Credentials JSON**. Specifying the `SecretStr` type displays the field's text with asterisks. + - The `model` field specifies the model for VertexAI to use. The field in the UI will have the title of **Model**. The default value for this field is `gemini-1.5-flash`. - At run time, the `PluginSettings` class reads these field's values from the UI and writes them as a JSON dictionary into a `settings.json` file - in the project's root. + in the project's root for the plugin to read from later.
@@ -324,9 +334,13 @@ In this section, you write the plugin's runtime logic. This tutorial's logic is ## Run plugin tests locally with pytest -In this section, you run the plugin's tests locally using `pytest` to make sure that the plugin's logic is working as expected before further +In this section, you manually run the plugin's tests locally using `pytest` to make sure that the plugin's logic is working as expected before further testing in Docker and eventual deployment for use in the UI. +In practice, you would typically use a continuous integration and continuous deployment (CI/CD) pipeline to automate running these tests. +If any of the tests fail, the pipeline should stop and notify you of the failure. If all of the tests pass, the pipeline should then +continue by [running the plugin in Docker](#run-the-plugin-in-docker-locally) as a further test. + 1. Add the necessary `pytest` dependencies. Also add a dependency on the `dotenv` package, which is used to read environment variables from a local `.env` file: @@ -363,7 +377,7 @@ testing in Docker and eventual deployment for use in the UI. raise ValueError("VERTEXAI_CREDENTIALS env var must be set to run test") settings_filepath: Path = tmp_path / "settings.json" - settings = {"location": "us-west1", "credentials": credentials} + settings = {"location": "us-east1", "credentials": credentials} settings_filepath.write_text(json.dumps(settings)) yield Plugin( @@ -375,7 +389,11 @@ testing in Docker and eventual deployment for use in the UI. ``` - The `plugin` function is a fixture that sets up the plugin's infrastructure for the `test_plugin` test function that follows. - - + - The function reads the `VERTEXAI_CREDENTIALS` environment variable from the `.env` file that you will create next. + - Instead of using the `settings.json` file that would normally be used by the `PluginSettings` class, the function creates a temporary + `settings.json` file just for these tests. This temporary file contains sample values for the **API Location** and **Credentials JSON** + fields that users would have + otherwise specified when using the plugin in the UI. 2. In the project's root, create a file named `.env`. In this file, add an environment variable named `VERTEXAI_CREDENTIALS`, and set it to the single-line representation of the `credentials.json` file that you generated in this tutorial's requirements: @@ -411,13 +429,14 @@ testing in Docker and eventual deployment for use in the UI. ``` - The `test_plugin` function is a test case that uses the `plugin` fixture to run the plugin's logic. - - The function takes a list of elements as input. The first element in the list contains the text that is used to test the plugin. + - The function takes a list of Unstructured-formatted elements as input. The first element in the list contains the text that is used to test the plugin. - The function then runs the plugin's logic and checks that the output is as expected. - The function checks that the output contains the expected values for the `toxicity`, `emotion`, and `intent` fields that are returned. If the expected values match, the test passes. Otherwise, the test fails. + - To run the test, use the following command to run `pytest` though the `test` target in a file named `Makefile` in the root of the project: + To run the test, use the following command to run `pytest` though the `test` target in the file named `Makefile` in the root of the project: ```bash make test @@ -436,8 +455,13 @@ testing in Docker and eventual deployment for use in the UI. ## Run the plugin in Docker locally -In this section, you proceed with local testing by running the plugin in Docker locally. -This allows you to test the plugin's logic in an isolated environment before you deploy it into your self-hosted UI. +In this section, you proceed with local testing by manually running the plugin in Docker locally. +This allows you to more fully test the plugin's logic in an isolated environment before you deploy it into your self-hosted UI. + +In practice, you would typically use a CI/CD pipeline to automate running the plugin in Docker and +testing the output against an expected result. If the plugin's output does not match the expected result, the pipeline should stop and notify you of the failure. +If the plugin's output matches the expected result, the pipeline should then continue by +[deploying the plugin to the staging version of your self-hosted Unstructured UI](#deploy-the-plugin-to-your-self-hosted-ui). @@ -453,10 +477,15 @@ This allows you to test the plugin's logic in an isolated environment before you In the preceding JSON: - - Replace `` with the location of the VertexAI API that you want to use, for example, `us-west1`. + - Replace `` with the location of the VertexAI API that you want to use, for example, `us-east1`. - Replace `` with the single-line representation of the `credentials.json` file that you generated in this tutorial's requirements. + + This `.vertex-plugin-settings.json` file contains sensitive information and + is intended for local Docker testing only. + + 1. In the file named `Makefile` in the root of the project, replace the `.PHONY: run-docker` definition with the following definition: @@ -472,7 +501,7 @@ This allows you to test the plugin's logic in an isolated environment before you "${IMAGE_REPOSITORY}:${VERSION}" ``` - - The `run-docker` target builds the Docker image locally and then runs it as a container representing the plugin. + The `run-docker` target builds the Docker image locally and then runs it as a container representing the plugin. 2. Start Docker Desktop on your local machine, if it is not already running. 3. Run the following command to call the `run-docker` target, which builds the Docker image and then runs the resulting container, representing the plugin: @@ -482,7 +511,7 @@ This allows you to test the plugin's logic in an isolated environment before you ``` You must leave this terminal window open and running while you are testing the plugin locally within the running Docker container. If you interrupt the running process here or close this - terminal window, the Docker container stops running and the plugin stops working. + terminal window, the Docker container stops running, and the plugin stops working. @@ -573,9 +602,10 @@ This allows you to test the plugin's logic in an isolated environment before you ## Deploy the plugin to your self-hosted UI -In this section, you deploy the successfully-tested plugin for your users to add to their workflows' DAGs within your self-hosted Unstructured UI. -This section describes how to deploy the plugin from your local development machine directly into your existing container registry. In practice, you -would typically use a continuous integration and continuous deployment (CI/CD) pipeline to automate this process through more robust means. +In this section, you manually deploy the successfully-tested plugin for your users to add to their workflows' DAGs within your self-hosted Unstructured UI. +This section describes how to deploy the plugin from your local development machine directly into your existing container registry. + +In practice, you would typically use a CI/CD pipeline to automate deploying the plugin. @@ -632,8 +662,9 @@ would typically use a continuous integration and continuous deployment (CI/CD) p - Run the following commands, one command at a time, to build the plugin's container and then deploy it to your container registry: - + Run the following commands, one command at a time, to build the plugin's container, deploy it to your container registry, and make the plugin + available for use in the staging version of your self-hosted Unstructured UI: + ```bash make docker-build make docker-push @@ -644,17 +675,48 @@ would typically use a continuous integration and continuous deployment (CI/CD) p -## Test the plugin in the UI +## Test the plugin in your UI - + + 1. Sign in to the staging version of your self-hosted Unstructured UI. + 2. Create a new workflow or open an existing workflow. + 3. In the workflow's visual DAG designer, click the `+` icon anywhere between a **Chunker** node and a **Destination** node, + and select **Plugins > Sentiment Analysis**. + 4. Click the **Sentiment Analysis** node to open its settings pane. + 5. In the settings pane, enter the required settings for the plugin. + For example, enter the location of the VertexAI API, the single-string version of the `credentials.json` file's contents for accessing the + VertexAI API, and the model for VertexAI to use. + 6. Run the workflow. + 7. When the workflow is finished, go to the destination location, and look for the `toxicity`, `emotion`, and `intent` values that the plugin adds to the `metadata` field for each element that Unstructured generated based on the source files' contents. - + + If you need to make any changes to the plugin, you can do so by returning to the previous section titled [Write the plugin](#write-the-plugin). + + Make the necessary code changes and then: + + 1. [Run plugin tests locally with pytest](#run-plugin-tests-locally-with-pytest). + 2. [Run the plugin in Docker locally](#run-the-plugin-in-docker-locally). + 3. [Deploy the plugin again to the staging version of your self-hosted Unstructured UI](#deploy-the-plugin-to-your-self-hosted-ui). + 4. Test the updated plugin again in staging. + + Keep repeating this loop until you are satisfied with the plugin's performance in staging. - + + After you have tested the plugin in your staging UI and are satisfied with its performance, you can promote it from staging to production. + To do this, run the following command: -## Learn more + ```bash + make promote-plugin-to-production + ``` + + Of coursse, you should immediately sign in to the production version of your self-hosted Unstructured UI and test the plugin from there + there before you start advertising its availability to your users. + + +Congratulations! You have successfully created, tested, and deployed your first custom plugin into your self-hosted Unstructured UI that your users +can now add to their workflow DAGs to unlock new capabilities and insights for their files and data! From 2dd2cc3b0e651a97a0b25f3af98dfdee445dd510 Mon Sep 17 00:00:00 2001 From: Paul Cornell Date: Wed, 2 Apr 2025 13:37:28 -0700 Subject: [PATCH 5/5] pytest and deployment versioning updates --- self-hosted/plugins/tutorial.mdx | 47 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/self-hosted/plugins/tutorial.mdx b/self-hosted/plugins/tutorial.mdx index 86af6c1c..01be8e5b 100644 --- a/self-hosted/plugins/tutorial.mdx +++ b/self-hosted/plugins/tutorial.mdx @@ -247,23 +247,27 @@ In this section, you write the plugin's runtime logic. This tutorial's logic is ```python def __post_init__(self): - with open(self.env_settings.job_settings_file) as f: - self.plugin_settings = PluginSettings.model_validate_json(f.read()) + try: + with open(self.env_settings.job_settings_file) as f: + self.plugin_settings = PluginSettings.model_validate_json(f.read()) - credentials_json = json.loads( - self.plugin_settings.credentials.get_secret_value() - ) - credentials = service_account.Credentials.from_service_account_info( - credentials_json - ) + credentials_json = json.loads( + self.plugin_settings.credentials.get_secret_value() + ) + credentials = service_account.Credentials.from_service_account_info( + credentials_json + ) - aiplatform.init( - location=self.plugin_settings.location, - project=credentials_json["project_id"], - credentials=credentials, - ) + aiplatform.init( + location=self.plugin_settings.location, + project=credentials_json["project_id"], + credentials=credentials, + ) - self.model = GenerativeModel(self.plugin_settings.model) + self.model = GenerativeModel(self.plugin_settings.model) + except Exception as e: + print(f"Plugin initialization failed: {e}") + raise ``` - The `__post_init__` function is called after the `Plugin` class is initialized. The function reads in the UI field values from the `settings.json` file @@ -365,6 +369,9 @@ continue by [running the plugin in Docker](#run-the-plugin-in-docker-locally) as from src.plugin_sentiment_analysis import Plugin, PluginSettings ``` + 4. In the root of the project's `test` directory, add a blank `__init__.py` file. This file is required to allow the + `src` directory to be seen by the `test` directory to enable the preceding `from...import` statement to work. + 1. In the `test_plugin.py` file, replace the `plugin` function body with the following definition: @@ -402,12 +409,12 @@ continue by [running the plugin in Docker](#run-the-plugin-in-docker-locally) as VERTEXAI_CREDENTIALS="" ``` - + If you plan to publish this plugin's source code to an external repository such as GitHub, do not include the `.env` file in the repository, as it can expose sentitive information publicly, such as your credentials for the VertexAI API. To help prevent this file from accidentally being included in the repository, add a `.env` entry to a `.gitignore` file in the root of the project. - + 3. In the `test_plugin.py` file, replace the `test_plugin` function body with the following definition. The function body definition should now look as follows: @@ -481,10 +488,10 @@ If the plugin's output matches the expected result, the pipeline should then con - Replace `` with the single-line representation of the `credentials.json` file that you generated in this tutorial's requirements. - + This `.vertex-plugin-settings.json` file contains sensitive information and - is intended for local Docker testing only. - + is intended for local Docker testing only. Do not check in this file with your plugin's source code. + @@ -698,6 +705,8 @@ In practice, you would typically use a CI/CD pipeline to automate deploying the 1. [Run plugin tests locally with pytest](#run-plugin-tests-locally-with-pytest). 2. [Run the plugin in Docker locally](#run-the-plugin-in-docker-locally). + 3. Increment the plugin's version number. To do this, in the project's `src/plugin_sentiment_analyis/__init__.py` file, + update the value of `version` in the `PLUGIN_MANIFEST` variable, for example from `0.0.1` to `0.0.2`. Then save this file. 3. [Deploy the plugin again to the staging version of your self-hosted Unstructured UI](#deploy-the-plugin-to-your-self-hosted-ui). 4. Test the updated plugin again in staging.